package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/xuri/excelize/v2"
)

func templateColumns(raw string) ([]string, map[string]string, error) {
	labels := map[string]string{}
	decoder := json.NewDecoder(strings.NewReader(raw))
	token, err := decoder.Token()
	if err != nil || token != json.Delim('{') {
		return nil, nil, err
	}
	keys := make([]string, 0)
	for decoder.More() {
		keyToken, tokenErr := decoder.Token()
		if tokenErr != nil {
			return nil, nil, tokenErr
		}
		key := keyToken.(string)
		var label string
		if tokenErr = decoder.Decode(&label); tokenErr != nil {
			return nil, nil, tokenErr
		}
		keys = append(keys, key)
		labels[key] = label
	}
	if _, err = decoder.Token(); err != nil {
		return nil, nil, err
	}
	return keys, labels, nil
}
func (s *ExportService) Export(ctx context.Context, tid string, params map[string]string) ([]byte, string, error) {
	template, err := s.uc.FindExportTemplate(ctx, 0, tid)
	if err != nil {
		return nil, "", err
	}
	rows, _, err := s.uc.QueryExport(ctx, template, params)
	if err != nil {
		return nil, "", err
	}
	keys, labels, err := templateColumns(template.TemplateInfo)
	if err != nil {
		return nil, "", err
	}
	file := excelize.NewFile()
	sheet := file.GetSheetName(0)
	for column, key := range keys {
		cell, _ := excelize.CoordinatesToCellName(column+1, 1)
		_ = file.SetCellValue(sheet, cell, labels[key])
	}
	for rowIndex, row := range rows {
		for column, key := range keys {
			cell, _ := excelize.CoordinatesToCellName(column+1, rowIndex+2)
			lookup := strings.ReplaceAll(strings.ReplaceAll(key, `"`, ""), "`", "")
			if len(template.Joins) > 0 {
				if parts := strings.Split(lookup, " as "); len(parts) > 1 {
					lookup = strings.TrimSpace(parts[1])
				} else if parts = strings.Split(lookup, "."); len(parts) > 1 {
					lookup = parts[1]
				}
			}
			value := fmt.Sprintf("%v", row[lookup])
			if timestamp, ok := row[lookup].(time.Time); ok {
				value = timestamp.Format("2006-01-02 15:04:05")
			}
			if number, parseErr := strconv.ParseFloat(value, 64); parseErr == nil {
				_ = file.SetCellValue(sheet, cell, number)
			} else {
				_ = file.SetCellValue(sheet, cell, value)
			}
		}
	}
	buffer, err := file.WriteToBuffer()
	_ = file.Close()
	if err != nil {
		return nil, "", err
	}
	return buffer.Bytes(), template.Name + ".xlsx", nil
}
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
	template, err := s.uc.FindExportTemplate(ctx, 0, tid)
	if err != nil {
		return nil, "", err
	}
	keys, labels, err := templateColumns(template.TemplateInfo)
	if err != nil {
		return nil, "", err
	}
	file := excelize.NewFile()
	sheet := file.GetSheetName(0)
	for column, key := range keys {
		cell, _ := excelize.CoordinatesToCellName(column+1, 1)
		_ = file.SetCellValue(sheet, cell, labels[key])
	}
	buffer, err := file.WriteToBuffer()
	_ = file.Close()
	if err != nil {
		return nil, "", err
	}
	return buffer.Bytes(), template.Name + "_template.xlsx", nil
}
func (s *ExportService) Preview(ctx context.Context, tid string, params map[string]string) (string, error) {
	template, err := s.uc.FindExportTemplate(ctx, 0, tid)
	if err != nil {
		return "", err
	}
	return s.uc.PreviewExport(ctx, template, params)
}
func (s *ExportService) Import(ctx context.Context, tid string, reader io.Reader) error {
	template, err := s.uc.FindExportTemplate(ctx, 0, tid)
	if err != nil {
		return err
	}
	file, err := excelize.OpenReader(reader)
	if err != nil {
		return err
	}
	defer file.Close()
	rows, err := file.GetRows("Sheet1")
	if err != nil {
		return err
	}
	if len(rows) < 2 {
		return errors.New("Excel data is not enough.\nIt should contain title row and data")
	}
	keys, labels, err := templateColumns(template.TemplateInfo)
	if err != nil {
		return err
	}
	reverse := map[string]string{}
	for _, key := range keys {
		reverse[labels[key]] = key
	}
	headers := rows[0]
	for index := range headers {
		headers[index] = strings.TrimSpace(headers[index])
	}
	items := make([]map[string]any, 0, len(rows)-1)
	for _, row := range rows[1:] {
		item := map[string]any{}
		for column, value := range row {
			if column >= len(headers) {
				continue
			}
			key := reverse[headers[column]]
			if key != "" {
				item[key] = value
			}
		}
		items = append(items, item)
	}
	return s.uc.ImportExportRows(ctx, template, items)
}
