package controller

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"

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

func exportRecordingListExcel(c *fiber.Ctx, items []dto.RecordingListDTO) error {
	file := excelize.NewFile()
	defer file.Close()

	const sheetName = "Recordings"
	defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
	if defaultSheet != sheetName {
		if err := file.SetSheetName(defaultSheet, sheetName); err != nil {
			return fiber.NewError(fiber.StatusInternalServerError, "failed to prepare excel sheet")
		}
	}

	if err := setRecordingExportColumns(file, sheetName); err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "failed to prepare excel columns")
	}
	if err := setRecordingExportHeaders(file, sheetName); err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "failed to prepare excel headers")
	}
	if err := setRecordingExportRows(file, sheetName, items); err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "failed to prepare excel rows")
	}

	buffer, err := file.WriteToBuffer()
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
	}

	filename := fmt.Sprintf("recordings_%s.xlsx", time.Now().Format("20060102_150405"))
	c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))

	return c.Status(fiber.StatusOK).Send(buffer.Bytes())
}

func setRecordingExportColumns(file *excelize.File, sheet string) error {
	columnWidths := map[string]float64{
		"A":  6,
		"B":  18,
		"C":  24,
		"D":  18,
		"E":  10,
		"F":  12,
		"G":  20,
		"H":  18,
		"I":  16,
		"J":  12,
		"K":  12,
		"L":  16,
		"M":  16,
		"N":  18,
		"O":  18,
		"P":  16,
		"Q":  16,
		"R":  16,
		"S":  16,
		"T":  16,
		"U":  16,
		"V":  16,
		"W":  18,
		"X":  18,
		"Y":  18,
		"Z":  22,
		"AA": 16,
		"AB": 18,
	}

	for col, width := range columnWidths {
		if err := file.SetColWidth(sheet, col, col, width); err != nil {
			return err
		}
	}

	if err := file.SetRowHeight(sheet, 1, 30); err != nil {
		return err
	}
	if err := file.SetRowHeight(sheet, 2, 30); err != nil {
		return err
	}

	return nil
}

func setRecordingExportHeaders(file *excelize.File, sheet string) error {
	verticalHeaderCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "Y", "Z", "AA", "AB"}
	for _, col := range verticalHeaderCols {
		if err := file.MergeCell(sheet, col+"1", col+"2"); err != nil {
			return err
		}
	}

	headerValues := map[string]string{
		"A1":  "No",
		"B1":  "Lokasi",
		"C1":  "Flock",
		"D1":  "Kandang",
		"E1":  "Periode",
		"F1":  "Kategori",
		"G1":  "Umur (hari)",
		"H1":  "Waktu Recording",
		"I1":  "Populasi Akhir",
		"Y1":  "Status Approval",
		"Z1":  "Catatan Approval",
		"AA1": "Dibuat Oleh",
		"AB1": "Tanggal Submit",
	}
	for cell, value := range headerValues {
		if err := file.SetCellValue(sheet, cell, value); err != nil {
			return err
		}
	}

	if err := file.MergeCell(sheet, "J1", "K1"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "J1", "FCR"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "J2", "Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "K2", "Standard"); err != nil {
		return err
	}

	if err := file.MergeCell(sheet, "L1", "M1"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "L1", "Feed Intake (KG)"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "L2", "Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "M2", "Standard"); err != nil {
		return err
	}

	if err := file.MergeCell(sheet, "N1", "P1"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "N1", "Mortality"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "N2", "Cum Depletion Rate"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "O2", "Max Depletion Std"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "P2", "Total Depletion"); err != nil {
		return err
	}

	if err := file.MergeCell(sheet, "Q1", "T1"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "Q1", "Egg Production"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "Q2", "Egg Mass Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "R2", "Egg Mass Standar"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "S2", "Egg Weight Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "T2", "Egg Weight Standar"); err != nil {
		return err
	}

	if err := file.MergeCell(sheet, "U1", "X1"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "U1", "Hen Performance"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "U2", "Hen Day Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "V2", "Hen Day Standar"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "W2", "Hen House Actual"); err != nil {
		return err
	}
	if err := file.SetCellValue(sheet, "X2", "Hen House Standar"); err != nil {
		return err
	}

	headerStyle, err := file.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Bold:  true,
			Color: "7A7A7A",
		},
		Fill: excelize.Fill{
			Type:    "pattern",
			Pattern: 1,
			Color:   []string{"F5F5F5"},
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
			WrapText:   true,
		},
		Border: []excelize.Border{
			{Type: "left", Color: "DDDDDD", Style: 1},
			{Type: "top", Color: "DDDDDD", Style: 1},
			{Type: "bottom", Color: "DDDDDD", Style: 1},
			{Type: "right", Color: "DDDDDD", Style: 1},
		},
	})
	if err != nil {
		return err
	}

	return file.SetCellStyle(sheet, "A1", "AB2", headerStyle)
}

func setRecordingExportRows(file *excelize.File, sheet string, items []dto.RecordingListDTO) error {
	if len(items) == 0 {
		return nil
	}

	columns := []string{
		"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
		"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB",
	}

	for i, item := range items {
		rowNumber := i + 3

		fcrStd := 0.0
		if item.ProjectFlock.Fcr != nil {
			fcrStd = item.ProjectFlock.Fcr.FcrStd
		}

		maxDepletionStd := 0.0
		eggMassStd := 0.0
		eggWeightStd := 0.0
		henDayStd := 0.0
		henHouseStd := 0.0
		feedIntakeStd := 0.0
		if item.ProjectFlock.ProductionStandart != nil {
			maxDepletionStd = item.ProjectFlock.ProductionStandart.MaxDepletionStd
			eggMassStd = item.ProjectFlock.ProductionStandart.EggMassStd
			eggWeightStd = item.ProjectFlock.ProductionStandart.EggWeightStd
			henDayStd = item.ProjectFlock.ProductionStandart.HenDayStd
			henHouseStd = item.ProjectFlock.ProductionStandart.HenHouseStd
			feedIntakeStd = item.ProjectFlock.ProductionStandart.FeedIntakeStd
		}

		locationName := "-"
		if item.Location != nil {
			locationName = safeExportText(item.Location.Name)
		}

		kandangName := "-"
		if item.Kandang != nil {
			kandangName = safeExportText(item.Kandang.Name)
		}

		createdBy := "-"
		if item.CreatedUser != nil {
			createdBy = safeExportText(item.CreatedUser.Name)
		} else if strings.TrimSpace(item.Approval.ActionBy.Name) != "" {
			createdBy = safeExportText(item.Approval.ActionBy.Name)
		}

		rowValues := []interface{}{
			i + 1,
			locationName,
			safeExportText(item.ProjectFlock.FlockName),
			kandangName,
			item.ProjectFlock.Period,
			formatCategoryLabel(item.ProjectFlock.ProjectFlockCategory),
			formatAgeLabel(item),
			formatDateIndonesian(item.RecordDatetime),
			formatNumberID(item.ProjectFlock.TotalChickQty, 0, false),
			formatNumberID(item.FcrValue, 2, true),
			formatNumberID(fcrStd, 2, true),
			formatNumberID(item.FeedIntake, 2, true),
			formatNumberID(feedIntakeStd, 2, true),
			formatPercentID(item.CumDepletionRate, 2),
			formatPercentID(maxDepletionStd, 2),
			formatNumberID(item.TotalDepletionQty, 2, true),
			formatNumberID(item.EggMass, 2, true),
			formatNumberID(eggMassStd, 2, true),
			formatNumberID(item.EggWeight, 2, true),
			formatNumberID(eggWeightStd, 2, true),
			formatPercentID(item.HenDay, 2),
			formatPercentID(henDayStd, 2),
			formatPercentID(item.HenHouse, 2),
			formatPercentID(henHouseStd, 2),
			formatApprovalStatus(item),
			safeExportText(pointerString(item.Approval.Notes)),
			createdBy,
			formatDateIndonesian(item.CreatedAt),
		}

		for idx, col := range columns {
			cell := fmt.Sprintf("%s%d", col, rowNumber)
			if err := file.SetCellValue(sheet, cell, rowValues[idx]); err != nil {
				return err
			}
		}
	}

	lastRow := len(items) + 2
	dataCenterStyle, err := file.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
			WrapText:   true,
		},
		Border: []excelize.Border{
			{Type: "left", Color: "E6E6E6", Style: 1},
			{Type: "top", Color: "E6E6E6", Style: 1},
			{Type: "bottom", Color: "E6E6E6", Style: 1},
			{Type: "right", Color: "E6E6E6", Style: 1},
		},
	})
	if err != nil {
		return err
	}
	if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AB%d", lastRow), dataCenterStyle); err != nil {
		return err
	}

	dataLeftStyle, err := file.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "left",
			Vertical:   "center",
			WrapText:   true,
		},
		Border: []excelize.Border{
			{Type: "left", Color: "E6E6E6", Style: 1},
			{Type: "top", Color: "E6E6E6", Style: 1},
			{Type: "bottom", Color: "E6E6E6", Style: 1},
			{Type: "right", Color: "E6E6E6", Style: 1},
		},
	})
	if err != nil {
		return err
	}

	leftColumns := []string{"B", "C", "D", "F", "G", "H", "Y", "Z", "AA", "AB"}
	for _, col := range leftColumns {
		if err := file.SetCellStyle(sheet, col+"3", fmt.Sprintf("%s%d", col, lastRow), dataLeftStyle); err != nil {
			return err
		}
	}

	return nil
}

func formatAgeLabel(item dto.RecordingListDTO) string {
	if item.Day <= 0 {
		return "-"
	}

	week := 0
	if item.ProjectFlock.ProductionStandart != nil && item.ProjectFlock.ProductionStandart.Week > 0 {
		week = item.ProjectFlock.ProductionStandart.Week
	} else {
		week = ((item.Day - 1) / 7) + 1
	}

	return fmt.Sprintf("%d (Minggu ke-%d)", item.Day, week)
}

func formatDateIndonesian(t time.Time) string {
	if t.IsZero() {
		return "-"
	}

	loc, err := time.LoadLocation("Asia/Jakarta")
	if err == nil {
		t = t.In(loc)
	}

	monthNames := []string{
		"",
		"Januari",
		"Februari",
		"Maret",
		"April",
		"Mei",
		"Juni",
		"Juli",
		"Agustus",
		"September",
		"Oktober",
		"November",
		"Desember",
	}

	month := int(t.Month())
	monthLabel := strconv.Itoa(month)
	if month > 0 && month < len(monthNames) {
		monthLabel = monthNames[month]
	}

	return fmt.Sprintf("%02d %s %d", t.Day(), monthLabel, t.Year())
}

func formatCategoryLabel(value string) string {
	normalized := strings.TrimSpace(strings.ReplaceAll(value, "_", " "))
	if normalized == "" {
		return "-"
	}

	parts := strings.Fields(strings.ToLower(normalized))
	for i, part := range parts {
		if len(part) == 0 {
			continue
		}
		parts[i] = strings.ToUpper(part[:1]) + part[1:]
	}

	return strings.Join(parts, " ")
}

func formatPercentID(value float64, decimals int) string {
	return fmt.Sprintf("%s%%", formatNumberID(value, decimals, false))
}

func formatNumberID(value float64, decimals int, trim bool) string {
	if math.IsNaN(value) || math.IsInf(value, 0) {
		return "0"
	}
	if decimals < 0 {
		decimals = 0
	}

	raw := strconv.FormatFloat(value, 'f', decimals, 64)
	if trim && strings.Contains(raw, ".") {
		raw = strings.TrimRight(raw, "0")
		raw = strings.TrimRight(raw, ".")
	}

	parts := strings.SplitN(raw, ".", 2)
	intPart := parts[0]
	sign := ""
	if strings.HasPrefix(intPart, "-") {
		sign = "-"
		intPart = strings.TrimPrefix(intPart, "-")
	}
	if intPart == "" {
		intPart = "0"
	}

	var grouped strings.Builder
	rem := len(intPart) % 3
	if rem > 0 {
		grouped.WriteString(intPart[:rem])
		if len(intPart) > rem {
			grouped.WriteString(".")
		}
	}
	for i := rem; i < len(intPart); i += 3 {
		grouped.WriteString(intPart[i : i+3])
		if i+3 < len(intPart) {
			grouped.WriteString(".")
		}
	}

	result := sign + grouped.String()
	if len(parts) == 2 && parts[1] != "" {
		result += "," + parts[1]
	}

	return result
}

func safeExportText(value string) string {
	normalized := strings.TrimSpace(value)
	if normalized == "" {
		return "-"
	}
	return normalized
}

func pointerString(value *string) string {
	if value == nil {
		return ""
	}
	return *value
}

func formatApprovalStatus(item dto.RecordingListDTO) string {
	action := strings.ToUpper(strings.TrimSpace(pointerString(item.Approval.Action)))
	switch action {
	case "UPDATED":
		return "Diperbarui"
	case "CREATED":
		return safeExportText(item.Approval.StepName)
	default:
		return safeExportText(item.Approval.StepName)
	}
}
