package controller

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

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
	"gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/dto"
	locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
	supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"

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

const expenseExportSheetName = "Expenses"

func isAllExpenseExcelExportRequest(c *fiber.Ctx) bool {
	return strings.EqualFold(strings.TrimSpace(c.Query("export")), "excel") &&
		strings.EqualFold(strings.TrimSpace(c.Query("type")), "all")
}

func exportExpenseListExcel(c *fiber.Ctx, items []dto.ExpenseListDTO) error {
	content, err := buildExpenseExportWorkbook(items)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "failed to generate excel file")
	}

	filename := fmt.Sprintf("expenses_all_%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(content)
}

func buildExpenseExportWorkbook(items []dto.ExpenseListDTO) ([]byte, error) {
	file := excelize.NewFile()
	defer file.Close()

	defaultSheet := file.GetSheetName(file.GetActiveSheetIndex())
	if defaultSheet != expenseExportSheetName {
		if err := file.SetSheetName(defaultSheet, expenseExportSheetName); err != nil {
			return nil, err
		}
	}

	if err := setExpenseExportColumns(file, expenseExportSheetName); err != nil {
		return nil, err
	}
	if err := setExpenseExportHeaders(file, expenseExportSheetName); err != nil {
		return nil, err
	}
	if err := setExpenseExportRows(file, expenseExportSheetName, items); err != nil {
		return nil, err
	}
	if err := file.SetPanes(expenseExportSheetName, &excelize.Panes{
		Freeze:      true,
		YSplit:      1,
		TopLeftCell: "A2",
		ActivePane:  "bottomLeft",
	}); err != nil {
		return nil, err
	}

	buffer, err := file.WriteToBuffer()
	if err != nil {
		return nil, err
	}
	return buffer.Bytes(), nil
}

func setExpenseExportColumns(file *excelize.File, sheet string) error {
	columnWidths := map[string]float64{
		"A": 8,
		"B": 16,
		"C": 20,
		"D": 18,
		"E": 18,
		"F": 16,
		"G": 24,
		"H": 22,
		"I": 16,
		"J": 24,
	}

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

	return file.SetRowHeight(sheet, 1, 24)
}

func setExpenseExportHeaders(file *excelize.File, sheet string) error {
	headers := []string{
		"No",
		"No. PO",
		"No. Referensi",
		"Tanggal Realisasi",
		"Tanggal Transaksi",
		"Kategori",
		"Produk",
		"Lokasi",
		"Grand Total",
		"Status",
	}

	for i, header := range headers {
		colName, err := excelize.ColumnNumberToName(i + 1)
		if err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, colName+"1", header); err != nil {
			return err
		}
	}

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

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

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

	for i, item := range items {
		row := strconv.Itoa(i + 2)
		if err := file.SetCellValue(sheet, "A"+row, i+1); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "B"+row, safeExpenseExportText(item.PoNumber)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "C"+row, safeExpenseExportText(item.ReferenceNumber)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "D"+row, formatExpenseExportDate(item.RealizationDate)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "E"+row, formatExpenseExportDate(&item.TransactionDate)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "F"+row, safeExpenseExportText(item.Category)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "G"+row, safeExpenseSupplierName(item.Supplier)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "H"+row, safeExpenseLocationName(item.Location)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "I"+row, safeExpenseExportNumber(item.GrandTotal)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "J"+row, formatExpenseExportStatus(item.LatestApproval)); err != nil {
			return err
		}
	}

	lastRow := len(items) + 1

	dataStyle, err := file.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "left",
			Vertical:   "center",
			WrapText:   false,
		},
		Border: []excelize.Border{
			{Type: "left", Color: "D1D5DB", Style: 1},
			{Type: "top", Color: "D1D5DB", Style: 1},
			{Type: "bottom", Color: "D1D5DB", Style: 1},
			{Type: "right", Color: "D1D5DB", Style: 1},
		},
	})
	if err != nil {
		return err
	}
	if err := file.SetCellStyle(sheet, "A2", "J"+strconv.Itoa(lastRow), dataStyle); err != nil {
		return err
	}

	ordinalStyle, err := file.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
		},
		Border: []excelize.Border{
			{Type: "left", Color: "D1D5DB", Style: 1},
			{Type: "top", Color: "D1D5DB", Style: 1},
			{Type: "bottom", Color: "D1D5DB", Style: 1},
			{Type: "right", Color: "D1D5DB", Style: 1},
		},
	})
	if err != nil {
		return err
	}
	if err := file.SetCellStyle(sheet, "A2", "A"+strconv.Itoa(lastRow), ordinalStyle); err != nil {
		return err
	}

	numberFormat := "#,##0.##"
	numberStyle, err := file.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{
			Horizontal: "right",
			Vertical:   "center",
		},
		CustomNumFmt: &numberFormat,
		Border: []excelize.Border{
			{Type: "left", Color: "D1D5DB", Style: 1},
			{Type: "top", Color: "D1D5DB", Style: 1},
			{Type: "bottom", Color: "D1D5DB", Style: 1},
			{Type: "right", Color: "D1D5DB", Style: 1},
		},
	})
	if err != nil {
		return err
	}

	return file.SetCellStyle(sheet, "I2", "I"+strconv.Itoa(lastRow), numberStyle)
}

func formatExpenseExportDate(value *time.Time) string {
	if value == nil || value.IsZero() {
		return "-"
	}

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

	return t.Format("02-01-2006")
}

func formatExpenseExportStatus(latestApproval *approvalDTO.ApprovalRelationDTO) string {
	if latestApproval == nil {
		return "-"
	}

	if latestApproval.Action != nil &&
		strings.EqualFold(strings.TrimSpace(*latestApproval.Action), string(entity.ApprovalActionRejected)) {
		return "Ditolak"
	}

	return safeExpenseExportText(latestApproval.StepName)
}

func safeExpenseSupplierName(value *supplierDTO.SupplierRelationDTO) string {
	if value == nil {
		return "-"
	}
	return safeExpenseExportText(value.Name)
}

func safeExpenseLocationName(value *locationDTO.LocationRelationDTO) string {
	if value == nil {
		return "-"
	}
	return safeExpenseExportText(value.Name)
}

func safeExpenseExportNumber(value float64) float64 {
	if math.IsNaN(value) || math.IsInf(value, 0) {
		return 0
	}
	return value
}

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