package repository

import (
	"amocrm/internal/models"
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"strconv"
	"sync"
)

type Repository struct {
	redisClient *redis.Client
	logger      *zap.Logger
}

type Deps struct {
	RedisClient *redis.Client
	Logger      *zap.Logger
}

func NewRepository(deps Deps) *Repository {
	return &Repository{
		redisClient: deps.RedisClient,
		logger:      deps.Logger,
	}
}

func (r *Repository) CachingDealToRedis(ctx context.Context, deps models.SaveDeal) error {
	key := "deal:" + strconv.FormatInt(deps.AnswerID, 10) + ":" + strconv.Itoa(int(deps.DealID))
	valueJson, err := json.Marshal(deps)
	if err != nil {
		return err
	}

	err = r.redisClient.Set(ctx, key, valueJson, 0).Err()
	if err != nil {
		return err
	}

	return nil
}

func (r *Repository) CachingLeadFieldsToRedis(ctx context.Context, answerID int64, leadFields []models.FieldsValues) error {
	key := strconv.FormatInt(answerID, 10)
	leadFieldsJson, err := json.Marshal(leadFields)
	if err != nil {
		return err
	}

	err = r.redisClient.Set(ctx, key, leadFieldsJson, 0).Err()
	if err != nil {
		return err
	}

	return nil
}

func (r *Repository) FetchingDeals(ctx context.Context) (map[string][]models.MappingDealsData, map[int32]models.ForRestoringData, error) {
	keys, err := r.redisClient.Keys(ctx, "deal:*").Result()
	if err != nil {
		r.logger.Error("error fetching keys from Redis", zap.Error(err))
		return nil, nil, err
	}

	var (
		mu                 sync.Mutex
		dealsDataForUpdate = make(map[string][]models.MappingDealsData)
		forRestoringMap    = make(map[int32]models.ForRestoringData)
		wg                 sync.WaitGroup
	)

	wg.Add(len(keys))

	for _, key := range keys {
		go func(key string) {
			defer wg.Done()

			saveDealJSON, err := r.redisClient.GetDel(ctx, key).Result()
			if err != nil {
				r.logger.Error("error getting saveDeal JSON from Redis", zap.Error(err))
				return
			}

			var saveDeal models.SaveDeal
			err = json.Unmarshal([]byte(saveDealJSON), &saveDeal)
			if err != nil {
				r.logger.Error("error unmarshal saveDeal JSON", zap.Error(err))
				return
			}
			answerIDStr := strconv.FormatInt(saveDeal.AnswerID, 10)

			leadFieldsJSON, err := r.redisClient.GetDel(ctx, answerIDStr).Result()
			if err != nil {
				r.logger.Error("error getting leadFields JSON from Redis", zap.Error(err))
				return
			}

			var leadFields []models.FieldsValues
			err = json.Unmarshal([]byte(leadFieldsJSON), &leadFields)
			if err != nil {
				r.logger.Error("error unmarshal leadFields JSON", zap.Error(err))
				return
			}

			fmt.Println("CUSTOM ENCODER", leadFields)

			mu.Lock()
			defer mu.Unlock()

			dealsDataForUpdate[saveDeal.AccessToken] = append(dealsDataForUpdate[saveDeal.AccessToken], models.MappingDealsData{
				AnswerID:   saveDeal.AnswerID,
				DealID:     saveDeal.DealID,
				LeadFields: leadFields,
				SubDomain:  saveDeal.SubDomain,
			})

			forRestoringMap[saveDeal.DealID] = models.ForRestoringData{
				SaveDeal:   saveDeal,
				LeadFields: leadFields,
			}
		}(key)
	}

	wg.Wait()

	return dealsDataForUpdate, forRestoringMap, nil
}

func (r *Repository) Close(_ context.Context) error {
	err := r.redisClient.Close()
	if err != nil {
		return err
	}

	return nil
}
