package service

import (
	"context"
	"encoding/json"
	"errors"
	"strings"
	"time"

	"kra/internal/biz"
	"kra/internal/service/dto"

	"github.com/google/uuid"
)

var (
	ErrExportTokenInvalid   = errors.New("export token invalid")
	ErrExportTokenMalformed = errors.New("export token malformed")
	ErrExportTokenType      = errors.New("export token type mismatch")
)

type ExportService struct {
	uc    *biz.ExportUsecase
	cache biz.Cache
}

type ExportToken struct {
	TemplateID string
	Params     map[string]string
	Blank      bool
}

type exportTokenCache struct {
	TemplateID string            `json:"templateID"`
	Params     map[string]string `json:"params"`
	Blank      bool              `json:"blank"`
}

func NewExportService(uc *biz.ExportUsecase, cache biz.Cache) *ExportService {
	return &ExportService{uc: uc, cache: cache}
}

func (s *ExportService) IssueToken(ctx context.Context, templateID string, params map[string]string, blank bool) (string, error) {
	token := strings.ReplaceAll(uuid.NewString(), "-", "")
	raw, err := json.Marshal(exportTokenCache{TemplateID: templateID, Params: params, Blank: blank})
	if err != nil {
		return "", err
	}
	if err = s.cache.Set(ctx, "export:"+token, string(raw), 30*time.Minute); err != nil {
		return "", err
	}
	return token, nil
}

func (s *ExportService) ConsumeToken(ctx context.Context, token string, expectBlank bool) (*ExportToken, error) {
	raw, ok, err := s.cache.Get(ctx, "export:"+token)
	if err != nil || !ok {
		return nil, ErrExportTokenInvalid
	}
	var value exportTokenCache
	if json.Unmarshal([]byte(raw), &value) != nil {
		return nil, ErrExportTokenMalformed
	}
	if value.Blank != expectBlank {
		return nil, ErrExportTokenType
	}
	_ = s.cache.Delete(ctx, "export:"+token)
	return &ExportToken{TemplateID: value.TemplateID, Params: value.Params, Blank: value.Blank}, nil
}
func exportTemplateDomain(value *dto.ExportTemplateRequest) *biz.ExportTemplate {
	out := &biz.ExportTemplate{ID: value.ID, DBName: value.DBName, Name: value.Name, TableName: value.TableName, TemplateID: value.TemplateID, TemplateInfo: value.TemplateInfo, SQL: value.SQL, ImportSQL: value.ImportSQL, Limit: value.Limit, Order: value.Order}
	for _, item := range value.Conditions {
		out.Conditions = append(out.Conditions, biz.ExportCondition{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, TemplateID: item.TemplateID, From: item.From, Column: item.Column, Operator: item.Operator})
	}
	for _, item := range value.Joins {
		out.Joins = append(out.Joins, biz.ExportJoin{ID: item.ID, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, TemplateID: item.TemplateID, Join: item.Join, Table: item.Table, On: item.On})
	}
	return out
}
func (s *ExportService) CreateRequest(ctx context.Context, req *dto.ExportTemplateRequest) error {
	return s.Create(ctx, exportTemplateDomain(req))
}
func (s *ExportService) UpdateRequest(ctx context.Context, req *dto.ExportTemplateRequest) error {
	return s.Update(ctx, exportTemplateDomain(req))
}
func (s *ExportService) TemplatesFilter(ctx context.Context, page, size int, name, tableName, templateID string, start, end *time.Time) ([]*dto.ExportTemplateResponse, int64, error) {
	return s.Templates(ctx, page, size, &biz.ExportTemplate{Name: name, TableName: tableName, TemplateID: templateID, StartCreatedAt: start, EndCreatedAt: end})
}
func exportDTO(v *biz.ExportTemplate) *dto.ExportTemplateResponse {
	var conditions []dto.ExportConditionResponse
	if v.Conditions != nil {
		conditions = make([]dto.ExportConditionResponse, 0, len(v.Conditions))
		for _, x := range v.Conditions {
			conditions = append(conditions, dto.ExportConditionResponse{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, From: x.From, Column: x.Column, Operator: x.Operator})
		}
	}
	var joins []dto.ExportJoinResponse
	if v.Joins != nil {
		joins = make([]dto.ExportJoinResponse, 0, len(v.Joins))
		for _, x := range v.Joins {
			joins = append(joins, dto.ExportJoinResponse{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, Join: x.Join, Table: x.Table, On: x.On})
		}
	}
	return &dto.ExportTemplateResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, DBName: v.DBName, Name: v.Name, TableName: v.TableName, TemplateID: v.TemplateID, TemplateInfo: v.TemplateInfo, SQL: v.SQL, ImportSQL: v.ImportSQL, Limit: v.Limit, Order: v.Order, Conditions: conditions, Joins: joins}
}
func (s *ExportService) Create(ctx context.Context, v *biz.ExportTemplate) error {
	return s.uc.CreateExportTemplate(ctx, v)
}
func (s *ExportService) Update(ctx context.Context, v *biz.ExportTemplate) error {
	return s.uc.UpdateExportTemplate(ctx, v)
}
func (s *ExportService) Delete(ctx context.Context, ids []uint) error {
	return s.uc.DeleteExportTemplates(ctx, ids)
}
func (s *ExportService) Template(ctx context.Context, id uint, tid string) (*dto.ExportTemplateResponse, error) {
	v, err := s.uc.FindExportTemplate(ctx, id, tid)
	if err != nil {
		return nil, err
	}
	return exportDTO(v), nil
}
func (s *ExportService) Templates(ctx context.Context, page, size int, q *biz.ExportTemplate) ([]*dto.ExportTemplateResponse, int64, error) {
	items, total, err := s.uc.ListExportTemplates(ctx, page, size, q)
	if err != nil {
		return nil, 0, err
	}
	out := make([]*dto.ExportTemplateResponse, 0, len(items))
	for _, v := range items {
		out = append(out, exportDTO(v))
	}
	return out, total, nil
}
