package controller

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

	"github.com/gofiber/fiber/v2"
	"github.com/xuri/excelize/v2"
	"gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto"
)

const marketingReportExportSheetName = "Laporan Marketing Harian"

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

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

	filename := fmt.Sprintf("laporan_marketing_harian_%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 buildMarketingReportWorkbook(items []dto.RepportMarketingItemDTO) ([]byte, error) {
	file := excelize.NewFile()
	defer file.Close()

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

	if err := setMarketingReportColumns(file); err != nil {
		return nil, err
	}
	if err := setMarketingReportHeaders(file); err != nil {
		return nil, err
	}
	if err := setMarketingReportRows(file, items); err != nil {
		return nil, err
	}

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

	return buffer.Bytes(), nil
}

func setMarketingReportColumns(file *excelize.File) error {
	columnWidths := map[string]float64{
		"A": 10,
		"B": 15,
		"C": 18,
		"D": 10,
		"E": 25,
		"F": 25,
		"G": 15,
		"H": 20,
		"I": 15,
		"J": 15,
		"K": 20,
		"L": 12,
		"M": 20,
		"N": 18,
		"O": 18,
		"P": 15,
		"Q": 20,
		"R": 20,
	}

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

	return nil
}

func setMarketingReportHeaders(file *excelize.File) error {
	sheet := marketingReportExportSheetName
	headers := []string{
		"No",
		"Tanggal Jual",
		"Tanggal Realisasi",
		"Aging",
		"Gudang Fisik",
		"Pelanggan",
		"No. DO",
		"Sales/Marketing",
		"No. Polisi",
		"Marketing Type",
		"Produk",
		"Kuantitas",
		"Bobot Rata-Rata (Kg)",
		"Bobot Total (Kg)",
		"Harga Jual (Rp)",
		"HPP (Rp)",
		"HPP Amount (Rp)",
		"Total (Rp)",
	}

	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
		}
	}

	return nil
}

func setMarketingReportRows(file *excelize.File, items []dto.RepportMarketingItemDTO) error {
	sheet := marketingReportExportSheetName
	summary := dto.ToSummaryFromDTOItems(items)

	for idx, item := range items {
		row := strconv.Itoa(idx + 2)

		warehouseName := "-"
		if item.Warehouse != nil {
			warehouseName = safeMarketingExportText(item.Warehouse.Name)
		}

		customerName := "-"
		if item.Customer != nil {
			customerName = safeMarketingExportText(item.Customer.Name)
		}

		salesName := "-"
		if item.Sales != nil {
			salesName = safeMarketingExportText(item.Sales.Name)
		}

		productName := "-"
		if item.Product != nil {
			productName = safeMarketingExportText(item.Product.Name)
		}

		agingText := fmt.Sprintf("%d hari", item.AgingDays)

		values := []interface{}{
			idx + 1,
			formatMarketingDate(item.SoDate),
			formatMarketingDate(item.RealizationDate),
			agingText,
			warehouseName,
			customerName,
			safeMarketingExportText(item.DoNumber),
			salesName,
			safeMarketingExportText(item.VehicleNumber),
			safeMarketingExportText(item.MarketingType),
			productName,
			item.Qty,
			item.AverageWeightKg,
			item.TotalWeightKg,
			formatMarketingRupiah(item.SalesPricePerKg),
			formatMarketingRupiah(item.HppPricePerKg),
			formatMarketingRupiah(item.HppAmount),
			formatMarketingRupiah(item.SalesAmount),
		}

		for colIdx, val := range values {
			colName, err := excelize.ColumnNumberToName(colIdx + 1)
			if err != nil {
				return err
			}
			if err := file.SetCellValue(sheet, colName+row, val); err != nil {
				return err
			}
		}
	}

	// Baris TOTAL
	totalRow := strconv.Itoa(len(items) + 2)
	if err := file.SetCellValue(sheet, "A"+totalRow, "TOTAL"); err != nil {
		return err
	}

	if summary != nil {
		if err := file.SetCellValue(sheet, "L"+totalRow, summary.TotalQty); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "M"+totalRow, summary.AverageWeightKg); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "N"+totalRow, summary.TotalWeightKg); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "O"+totalRow, formatMarketingRupiah(summary.AverageSalesPrice)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "P"+totalRow, formatMarketingRupiah(summary.TotalHppPricePerKg)); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "Q"+totalRow, formatMarketingRupiah(float64(summary.TotalHppAmount))); err != nil {
			return err
		}
		if err := file.SetCellValue(sheet, "R"+totalRow, formatMarketingRupiah(float64(summary.TotalSalesAmount))); err != nil {
			return err
		}
	}

	return nil
}

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

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

	return t.Format("02 Jan 2006")
}

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

// formatMarketingRupiah formats a float64 as Indonesian Rupiah string.
// e.g. 1000000 → "Rp 1.000.000"
func formatMarketingRupiah(value float64) string {
	rounded := int64(math.Round(value))

	negative := rounded < 0
	abs := rounded
	if negative {
		abs = -rounded
	}

	numStr := strconv.FormatInt(abs, 10)
	n := len(numStr)

	var b strings.Builder
	for i, c := range numStr {
		if i > 0 && (n-i)%3 == 0 {
			b.WriteByte('.')
		}
		b.WriteRune(c)
	}

	if negative {
		return "Rp -" + b.String()
	}
	return "Rp " + b.String()
}
