package store_repository

import (
	"bytes"
	"chonho/config"
	_const "chonho/const"
	"chonho/internal/api-v1/entity"
	"chonho/pkg/model"
	"chonho/util"
	"context"
	"encoding/json"
	"github.com/elastic/go-elasticsearch/v7"
	"log"
	"strconv"
)

func (r *repository) FindStoreEntity(filter StoreFilter) ([]entity.Store, error) {
	var result []entity.Store
	builder := r.db.Table("stores s").
		Select("s.*, u.phone AS seller_phone").
		Joins("JOIN users u ON u.id = s.user_id").
		Where("s.status = ? AND u.status = ? AND s.total_products > 0", _const.StoreStatusVerified, _const.UserStatusVerified)

	if filter.SearchString != "" {
		filter.StoreIds = r.getStoreIdsFromES(filter.SearchString)
		if len(filter.StoreIds) == 0 {
			return []entity.Store{}, nil
		}
	}

	if len(filter.StoreIds) > 0 {
		builder.Where("s.id IN ?", filter.StoreIds)
	}

	if filter.UserId != 0 {
		builder.Where("s.user_id = ?", filter.UserId)
	}

	if filter.ApartmentId != 0 {
		builder.Where("s.apartment_id = ?", filter.ApartmentId)
	}

	builder.Where("s.name NOT LIKE  '%test##%'")

	//if len(filter.SearchTerms) > 0 {
	//	for _, e := range filter.SearchTerms {
	//		builder.Where("s.keywords LIKE (?)", "%"+e+"%")
	//	}
	//}

	err := builder.Find(&result).Error
	if err != nil {
		return nil, err
	}

	for i, e := range result {
		if e.Category == nil {
			e.Category = []int64{}
		}
		_ = json.Unmarshal([]byte(e.Payment), &result[i].StorePayment)
		result[i].Rating = util.ToFixed(e.Rating, 2)
		result[i].CategoryIds = e.Category
		result[i].Phone = e.SellerPhone
		result[i].StorePayment = entity.AppendStorePaymentCOD(result[i].StorePayment)
	}
	return result, nil
}

func (r *repository) getStoreIdsFromES(keywords string) []int64 {
	// TODO return error if service down
	es, _ := elasticsearch.NewClient(elasticsearch.Config{
		Addresses: []string{
			config.ElasticSearch,
		},
	})

	// Build the request body.
	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"match": map[string]interface{}{
				"keywords": keywords,
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := es.Search(
		es.Search.WithContext(context.Background()),
		es.Search.WithIndex(_const.StoreIndex),
		es.Search.WithBody(&buf),
		es.Search.WithTrackTotalHits(true),
		es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	if res.IsError() {
		var e map[string]interface{}
		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
			log.Fatalf("Error parsing the response body: %s", err)
		} else {
			// Print the response status and error information.
			log.Fatalf("[%s] %s: %s",
				res.Status(),
				e["error"].(map[string]interface{})["type"],
				e["error"].(map[string]interface{})["reason"],
			)
		}
	}

	var ids []int64
	var rs map[string]interface{}
	if err := json.NewDecoder(res.Body).Decode(&rs); err != nil {
		log.Fatalf("Error parsing the response body: %s", err)
	}

	for _, hit := range rs["hits"].(map[string]interface{})["hits"].([]interface{}) {
		id, _ := strconv.ParseInt(hit.(map[string]interface{})["_id"].(string), 10, 64)
		ids = append(ids, id)
	}
	return ids
}

func (r *repository) FindStorePayment(filter StoreFilter) ([]entity.StorePaymentInfo, error) {
	var result []entity.StorePaymentInfo
	builder := r.db.Model(&model.Store{})

	if len(filter.StoreIds) > 0 {
		builder = builder.Where("id IN (?)", filter.StoreIds)
	}
	err := builder.Find(&result).Error
	if err != nil {
		return nil, err
	}
	for i, el := range result {
		var paymentInfo []entity.StorePayment
		if err := json.Unmarshal([]byte(el.Payment), &paymentInfo); err != nil {
			result[i].StorePayment = []entity.StorePayment{}
		} else {
			result[i].StorePayment = paymentInfo
		}
		if result[i].PaymentAcceptCash {
			result[i].StorePayment = entity.AppendStorePaymentCOD(result[i].StorePayment)
		}
	}
	return result, nil
}

func (r *repository) UpdateStoreRate(storeId int64) error {
	subQuery := r.db.Table("rates").
		Select("AVG(rates.rate)").
		Where("store_id = ?", storeId)
	return r.db.Model(&model.Store{}).Where("id = ?", storeId).UpdateColumn("rating", subQuery).Error
}
