package dailyreport

import (
	"bytes"
	"errors"
	"fmt"
	"sort"
	"strings"
	"system-altrak/internal/domain"
	setModule "system-altrak/internal/modules/setting"
	"system-altrak/pkg/utils"
	"time"

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

type DailyReportService interface {
	FetchHistoricalRecords(search, from, to string, branchID uint, role string) ([]domain.OperationalRecord, error)
	RevertVerification(id uint, branchID uint, role string) error
	MarkAsExported(ids []uint, branchID uint, exportedBy uint) error
	ExportExcel(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error)
	ExportPdf(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error)
	GetLastArchivedAt(branchID uint) (*time.Time, error)
	GetYearlySummary(year int, branchID uint) ([]byte, error)
}

type serviceImpl struct {
	repo    DailyReportRepository
	pdf     *utils.PdfGenerator
	setting setModule.SettingService
}

func NewService(repo DailyReportRepository, pdf *utils.PdfGenerator, setting ...setModule.SettingService) DailyReportService {
	var resolvedSetting setModule.SettingService
	if len(setting) > 0 {
		resolvedSetting = setting[0]
	}

	return &serviceImpl{repo: repo, pdf: pdf, setting: resolvedSetting}
}

func (s *serviceImpl) FetchHistoricalRecords(search, from, to string, branchID uint, role string) ([]domain.OperationalRecord, error) {
	return s.repo.ListHistoricalRecords(search, from, to, branchID, role)
}

func (s *serviceImpl) RevertVerification(id uint, branchID uint, role string) error {
	return s.repo.RevertVerification(id, branchID, role)
}

func (s *serviceImpl) MarkAsExported(ids []uint, branchID uint, exportedBy uint) error {
	return s.repo.MarkAsExported(ids, branchID, exportedBy)
}

func normalizeCustomerMode(mode string) string {
	switch strings.ToLower(strings.TrimSpace(mode)) {
	case "include", "exclude", "all":
		return strings.ToLower(strings.TrimSpace(mode))
	default:
		return "all"
	}
}

func normalizeCustomerName(value string) string {
	return strings.ToLower(strings.TrimSpace(value))
}

func filterRecordsByCustomers(records []domain.OperationalRecord, mode string, customerNames []string) []domain.OperationalRecord {
	mode = normalizeCustomerMode(mode)
	if mode == "all" {
		return records
	}

	selected := map[string]struct{}{}
	for _, name := range customerNames {
		if normalized := normalizeCustomerName(name); normalized != "" {
			selected[normalized] = struct{}{}
		}
	}
	if len(selected) == 0 {
		if mode == "exclude" {
			return records
		}
		return []domain.OperationalRecord{}
	}

	filtered := make([]domain.OperationalRecord, 0, len(records))
	for _, record := range records {
		_, isSelected := selected[normalizeCustomerName(record.CustomerName)]
		switch mode {
		case "exclude":
			if !isSelected {
				filtered = append(filtered, record)
			}
		default:
			if isSelected {
				filtered = append(filtered, record)
			}
		}
	}
	return filtered
}

func buildValidationPeriodLabel(from, to string, records []domain.OperationalRecord) string {
	formatInputDate := func(raw string) string {
		if raw == "" {
			return ""
		}
		parsed, err := time.Parse("2006-01-02", raw)
		if err != nil {
			return ""
		}
		return utils.FormatDateID(parsed)
	}

	formattedFrom := formatInputDate(from)
	formattedTo := formatInputDate(to)

	switch {
	case formattedFrom != "" && formattedTo != "":
		if formattedFrom == formattedTo {
			return formattedFrom
		}
		return fmt.Sprintf("%s - %s", formattedFrom, formattedTo)
	case formattedFrom != "":
		return formattedFrom
	case formattedTo != "":
		return formattedTo
	case len(records) > 0 && records[0].VerifiedAt != nil:
		return utils.FormatDateID(*records[0].VerifiedAt)
	default:
		return "-"
	}
}

func (s *serviceImpl) ExportExcel(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error) {
	records, err := s.repo.ListHistoricalRecords(search, from, to, branchID, role)
	if err != nil {
		return nil, err
	}
	records = filterRecordsByCustomers(records, customerMode, customerNames)

	// Sort records based on requested mode
	sort.Slice(records, func(i, j int) bool {
		if strings.EqualFold(sortMode, "customer") {
			return strings.ToLower(records[i].CustomerName) < strings.ToLower(records[j].CustomerName)
		}
		// Default: Sort by verified_at then customer_name
		timeI := time.Time{}
		if records[i].VerifiedAt != nil {
			timeI = *records[i].VerifiedAt
		}
		timeJ := time.Time{}
		if records[j].VerifiedAt != nil {
			timeJ = *records[j].VerifiedAt
		}
		if !timeI.Equal(timeJ) {
			return timeI.Before(timeJ)
		}
		return strings.ToLower(records[i].CustomerName) < strings.ToLower(records[j].CustomerName)
	})

	f := excelize.NewFile()
	defer f.Close()
	sheet := "Daily Report"
	f.SetSheetName("Sheet1", sheet)

	// Set Layout matching Image 2
	f.SetCellValue(sheet, "A1", "PT. ALTRAK 1978 BANJARMASIN")
	f.MergeCell(sheet, "A1", "I1")
	styleHeader, _ := f.NewStyle(&excelize.Style{Font: &excelize.Font{Bold: true, Size: 14}, Alignment: &excelize.Alignment{Horizontal: "center"}})
	f.SetCellStyle(sheet, "A1", "A1", styleHeader)

	f.SetCellValue(sheet, "A2", "PSO REPORT TO SI")
	f.MergeCell(sheet, "A2", "I2")
	f.SetCellStyle(sheet, "A2", "A2", styleHeader)

	// Use verification date range as export period label.
	perDate := buildValidationPeriodLabel(from, to, records)
	f.SetCellValue(sheet, "A3", fmt.Sprintf("PER %s", perDate))
	f.MergeCell(sheet, "A3", "I3")
	f.SetCellStyle(sheet, "A3", "A3", styleHeader)

	// Column Headers matching Image 2
	headers := []string{"No.", "NO PSO", "DATE", "NO PO", "NAMA CUSTOMER", "TOP", "DISC.", "CURR.", "AMOUNT"}
	styleTableHead, _ := f.NewStyle(&excelize.Style{
		Fill:      excelize.Fill{Type: "pattern", Color: []string{"40E0D0"}, Pattern: 1}, // Cyan/Turquoise from image
		Font:      &excelize.Font{Bold: true},
		Border:    []excelize.Border{{Type: "left", Color: "000000", Style: 1}, {Type: "right", Color: "000000", Style: 1}, {Type: "top", Color: "000000", Style: 1}, {Type: "bottom", Color: "000000", Style: 1}},
		Alignment: &excelize.Alignment{Horizontal: "center"},
	})

	for i, h := range headers {
		cell, _ := excelize.CoordinatesToCellName(i+1, 4)
		f.SetCellValue(sheet, cell, h)
		f.SetCellStyle(sheet, cell, cell, styleTableHead)
	}

	styleBorder, _ := f.NewStyle(&excelize.Style{
		Border: []excelize.Border{{Type: "left", Color: "000000", Style: 1}, {Type: "right", Color: "000000", Style: 1}, {Type: "top", Color: "000000", Style: 1}, {Type: "bottom", Color: "000000", Style: 1}},
	})
	styleAmount, _ := f.NewStyle(&excelize.Style{
		Border:       []excelize.Border{{Type: "left", Color: "000000", Style: 1}, {Type: "right", Color: "000000", Style: 1}, {Type: "top", Color: "000000", Style: 1}, {Type: "bottom", Color: "000000", Style: 1}},
		CustomNumFmt: func() *string { s := "#,##0"; return &s }(),
		Alignment:    &excelize.Alignment{Horizontal: "right"},
	})

	var total int64
	for i, v := range records {
		r := i + 5
		amount := v.Amount / 100
		total += amount

		f.SetCellValue(sheet, fmt.Sprintf("A%d", r), i+1)
		f.SetCellValue(sheet, fmt.Sprintf("B%d", r), v.PsoNo)
		if v.PsoDate != nil {
			f.SetCellValue(sheet, fmt.Sprintf("C%d", r), v.PsoDate.Format("02/01/2006"))
		}
		f.SetCellValue(sheet, fmt.Sprintf("D%d", r), v.PoNo)
		f.SetCellValue(sheet, fmt.Sprintf("E%d", r), v.CustomerName)
		f.SetCellValue(sheet, fmt.Sprintf("F%d", r), v.TOP)
		f.SetCellValue(sheet, fmt.Sprintf("G%d", r), v.Discount)
		f.SetCellValue(sheet, fmt.Sprintf("H%d", r), v.Currency)
		f.SetCellValue(sheet, fmt.Sprintf("I%d", r), amount)

		for j := 1; j <= 8; j++ {
			cell, _ := excelize.CoordinatesToCellName(j, r)
			f.SetCellStyle(sheet, cell, cell, styleBorder)
		}
		f.SetCellStyle(sheet, fmt.Sprintf("I%d", r), fmt.Sprintf("I%d", r), styleAmount)
	}

	// Total Row
	totalRow := len(records) + 5
	f.SetCellValue(sheet, fmt.Sprintf("I%d", totalRow), total)
	f.SetCellStyle(sheet, fmt.Sprintf("I%d", totalRow), fmt.Sprintf("I%d", totalRow), styleAmount)
	styleTotalFill, _ := f.NewStyle(&excelize.Style{
		Fill:   excelize.Fill{Type: "pattern", Color: []string{"90EE90"}, Pattern: 1}, // Light green from image
		Border: []excelize.Border{{Type: "left", Color: "000000", Style: 1}, {Type: "right", Color: "000000", Style: 1}, {Type: "top", Color: "000000", Style: 1}, {Type: "bottom", Color: "000000", Style: 1}},
	})
	for j := 1; j <= 8; j++ {
		cell, _ := excelize.CoordinatesToCellName(j, totalRow)
		f.SetCellStyle(sheet, cell, cell, styleTotalFill)
	}

	// Signatures matching Image 2
	sr := totalRow + 2
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr), "PENGIRIM")
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+1), "Nama")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+1), ": Yenni Permata Sari")
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+2), "Tanggal")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+2), fmt.Sprintf(": %s", perDate))
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+3), "TTD")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+3), ":")

	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+1), "Nama")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+1), ": Astri/ Indra")
	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+2), "Tanggal")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+2), fmt.Sprintf(": %s", perDate))
	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+3), "TTD")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+3), ":")
	f.SetCellValue(sheet, fmt.Sprintf("G%d", sr+3), "JAM :")
	f.SetCellValue(sheet, fmt.Sprintf("H%d", sr+3), ":")

	sr += 5
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr), "Mengetahui")
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+1), "Nama")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+1), ": Akhmad Husairi")
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+2), "Tanggal")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+2), fmt.Sprintf(": %s", perDate))
	f.SetCellValue(sheet, fmt.Sprintf("A%d", sr+3), "TTD")
	f.SetCellValue(sheet, fmt.Sprintf("B%d", sr+3), ":")

	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+1), "Nama")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+1), ": Martinda Taofik Akbar")
	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+2), "Tanggal")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+2), fmt.Sprintf(": %s", perDate))
	f.SetCellValue(sheet, fmt.Sprintf("E%d", sr+3), "TTD")
	f.SetCellValue(sheet, fmt.Sprintf("F%d", sr+3), ":")

	f.SetColWidth(sheet, "A", "A", 12)
	f.SetColWidth(sheet, "B", "B", 15)
	f.SetColWidth(sheet, "C", "C", 12)
	f.SetColWidth(sheet, "D", "D", 15)
	f.SetColWidth(sheet, "E", "E", 30)
	f.SetColWidth(sheet, "I", "I", 15)

	var buf bytes.Buffer
	err = f.Write(&buf)
	return buf.Bytes(), err
}

func (s *serviceImpl) ExportPdf(search, from, to, cutoffStr string, customerMode string, customerNames []string, branchID uint, role string, sortMode string) ([]byte, error) {
	records, err := s.repo.ListHistoricalRecords(search, from, to, branchID, role)
	if err != nil {
		return nil, err
	}
	records = filterRecordsByCustomers(records, customerMode, customerNames)

	// Sort records based on requested mode
	sort.Slice(records, func(i, j int) bool {
		if strings.EqualFold(sortMode, "customer") {
			return strings.ToLower(records[i].CustomerName) < strings.ToLower(records[j].CustomerName)
		}
		timeI := time.Time{}
		if records[i].VerifiedAt != nil {
			timeI = *records[i].VerifiedAt
		}
		timeJ := time.Time{}
		if records[j].VerifiedAt != nil {
			timeJ = *records[j].VerifiedAt
		}
		if !timeI.Equal(timeJ) {
			return timeI.Before(timeJ)
		}
		return strings.ToLower(records[i].CustomerName) < strings.ToLower(records[j].CustomerName)
	})

	// Format for display and calculate total
	var total int64
	for i := range records {
		records[i].Amount = records[i].Amount / 100
		total += records[i].Amount
	}

	perDate := buildValidationPeriodLabel(from, to, records)

	helper := utils.NewExportHelper(s.pdf, s.setting)
	data := map[string]interface{}{
		"Records":     records,
		"PerDate":     perDate,
		"TotalAmount": total,
		"Title":       "PSO REPORT TO SI",
	}

	return helper.GenerateStandardPDF("daily_reports/daily_report_pdf.html", data)
}
func (s *serviceImpl) GetLastArchivedAt(branchID uint) (*time.Time, error) {
	return s.repo.GetLastArchivedAt(branchID)
}

func (s *serviceImpl) GetYearlySummary(year int, branchID uint) ([]byte, error) {
	from := fmt.Sprintf("%d-01-01", year)
	to := fmt.Sprintf("%d-12-31", year)
	
	records, err := s.repo.ListHistoricalRecords("", from, to, branchID, "admin")
	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, errors.New("no records found for this year")
	}

	// Calculate statistics
	var totalAmount int64
	customers := make(map[string]struct{})
	for i := range records {
		records[i].Amount = records[i].Amount / 100
		totalAmount += records[i].Amount
		customers[records[i].CustomerName] = struct{}{}
	}

	data := map[string]interface{}{
		"Year":          year,
		"TotalAmount":   totalAmount,
		"CustomerCount": len(customers),
		"DocCount":      len(records),
		"Records":       records,
		"Title":         fmt.Sprintf("YEARLY SUMMARY REPORT - %d", year),
		"GeneratedAt":   time.Now().Format("02/01/2006 15:04"),
	}

	// We can use a special yearly template or the standard one
	helper := utils.NewExportHelper(s.pdf, s.setting)
	return helper.GenerateStandardPDF("yearly_summary_pdf.html", data)
}
