package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"bssapp-backend/utils"
	"bytes"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/jung-kurt/gofpdf"
	"github.com/xuri/excelize/v2"
)

type priceListExportRequest struct {
	// Product filters (same semantics as listing)
	ProductCode  []string `json:"product_code"`
	BrandGroup   []string `json:"brand_group"`
	AskiliYan    []string `json:"askili_yan"`
	Kategori     []string `json:"kategori"`
	UrunIlkGrubu []string `json:"urun_ilk_grubu"`
	UrunAnaGrubu []string `json:"urun_ana_grubu"`
	UrunAltGrubu []string `json:"urun_alt_grubu"`
	Icerik       []string `json:"icerik"`
	Karisim      []string `json:"karisim"`
	Marka        []string `json:"marka"`
	Search       string   `json:"search"`

	InStockOnly bool `json:"in_stock_only"`

	// Column selection
	IncludeMeta bool `json:"include_meta"`
	IncludeCost bool `json:"include_cost"`
	IncludeBase bool `json:"include_base"`

	USDLevels []int `json:"usd_levels"` // 1..6
	EURLevels []int `json:"eur_levels"` // 1..6
	TRYLevels []int `json:"try_levels"` // 1..6

	// Optional: explicit per-tier selection like ["usd1","eur3","try6"].
	// If provided, it overrides USDLevels/EURLevels/TRYLevels.
	PriceFields []string `json:"price_fields"`
}

type exportCol struct {
	Key   string
	Title string
	Width float64
	Align string // L/R/C for PDF
}

func cleanLevels(in []int) []int {
	out := make([]int, 0, len(in))
	seen := map[int]struct{}{}
	for _, v := range in {
		if v < 1 || v > 6 {
			continue
		}
		if _, ok := seen[v]; ok {
			continue
		}
		seen[v] = struct{}{}
		out = append(out, v)
	}
	sort.Ints(out)
	return out
}

func resolvePriceListColumns(req priceListExportRequest) []exportCol {
	cols := make([]exportCol, 0, 64)

	if req.IncludeMeta {
		cols = append(cols,
			exportCol{Key: "BrandGroupSec", Title: "MARKA GRUBU", Width: 26, Align: "L"},
			exportCol{Key: "Marka", Title: "MARKA", Width: 18, Align: "L"},
			exportCol{Key: "BrandCode", Title: "BRAND CODE", Width: 18, Align: "L"},
			exportCol{Key: "ProductCode", Title: "URUN KODU", Width: 22, Align: "L"},
			exportCol{Key: "StockQty", Title: "STOK ADET", Width: 16, Align: "R"},
			exportCol{Key: "StockEntryDate", Title: "STOK GIRIS", Width: 18, Align: "C"},
			exportCol{Key: "LastCostingDate", Title: "SON MALIYET", Width: 18, Align: "C"},
			exportCol{Key: "LastPricingDate", Title: "SON FIYAT", Width: 18, Align: "C"},
			exportCol{Key: "AskiliYan", Title: "ASKILI YAN", Width: 18, Align: "L"},
			exportCol{Key: "Kategori", Title: "KATEGORI", Width: 18, Align: "L"},
			exportCol{Key: "UrunIlkGrubu", Title: "URUN ILK GRUBU", Width: 20, Align: "L"},
			exportCol{Key: "UrunAnaGrubu", Title: "URUN ANA GRUBU", Width: 20, Align: "L"},
			exportCol{Key: "UrunAltGrubu", Title: "URUN ALT GRUBU", Width: 20, Align: "L"},
			exportCol{Key: "Icerik", Title: "ICERIK", Width: 18, Align: "L"},
			exportCol{Key: "Karisim", Title: "KARISIM", Width: 18, Align: "L"},
		)
	}
	if req.IncludeCost {
		cols = append(cols, exportCol{Key: "CostPrice", Title: "MALIYET FIYATI", Width: 16, Align: "R"})
	}
	if req.IncludeBase {
		cols = append(cols,
			exportCol{Key: "BasePriceUsd", Title: "TABAN USD", Width: 14, Align: "R"},
			exportCol{Key: "BasePriceTry", Title: "TABAN TRY", Width: 14, Align: "R"},
		)
	}

	// Explicit per-tier selection path (preserve order).
	if len(req.PriceFields) > 0 {
		seen := map[string]struct{}{}
		for _, raw := range req.PriceFields {
			v := strings.ToLower(strings.TrimSpace(raw))
			if v == "" {
				continue
			}
			switch v {
			case "usd1", "usd2", "usd3", "usd4", "usd5", "usd6",
				"eur1", "eur2", "eur3", "eur4", "eur5", "eur6",
				"try1", "try2", "try3", "try4", "try5", "try6":
				// ok
			default:
				continue
			}
			if _, ok := seen[v]; ok {
				continue
			}
			seen[v] = struct{}{}
			cur := strings.ToUpper(v[:3])
			lv := v[3:]
			key := fmt.Sprintf("%s%s", cur, lv)
			cols = append(cols, exportCol{Key: key, Title: fmt.Sprintf("%s %s", cur, lv), Width: 12, Align: "R"})
		}
		return cols
	}

	usd := cleanLevels(req.USDLevels)
	eur := cleanLevels(req.EURLevels)
	tr := cleanLevels(req.TRYLevels)
	for _, lv := range usd {
		cols = append(cols, exportCol{Key: fmt.Sprintf("USD%d", lv), Title: fmt.Sprintf("USD %d", lv), Width: 12, Align: "R"})
	}
	for _, lv := range eur {
		cols = append(cols, exportCol{Key: fmt.Sprintf("EUR%d", lv), Title: fmt.Sprintf("EUR %d", lv), Width: 12, Align: "R"})
	}
	for _, lv := range tr {
		cols = append(cols, exportCol{Key: fmt.Sprintf("TRY%d", lv), Title: fmt.Sprintf("TRY %d", lv), Width: 12, Align: "R"})
	}

	return cols
}

func fmtMoneyCell(v float64) string {
	if math.IsNaN(v) || math.IsInf(v, 0) {
		return ""
	}
	return fmt.Sprintf("%.2f", v)
}

func getCellValue(row models.ProductPricing, key string) string {
	switch key {
	case "BrandGroupSec":
		return strings.TrimSpace(row.BrandGroupSec)
	case "Marka":
		return strings.TrimSpace(row.Marka)
	case "BrandCode":
		return strings.TrimSpace(row.BrandCode)
	case "ProductCode":
		return strings.TrimSpace(row.ProductCode)
	case "StockQty":
		return fmtMoneyCell(row.StockQty)
	case "StockEntryDate":
		return strings.TrimSpace(row.StockEntryDate)
	case "LastCostingDate":
		return strings.TrimSpace(row.LastCostingDate)
	case "LastPricingDate":
		return strings.TrimSpace(row.LastPricingDate)
	case "AskiliYan":
		return strings.TrimSpace(row.AskiliYan)
	case "Kategori":
		return strings.TrimSpace(row.Kategori)
	case "UrunIlkGrubu":
		return strings.TrimSpace(row.UrunIlkGrubu)
	case "UrunAnaGrubu":
		return strings.TrimSpace(row.UrunAnaGrubu)
	case "UrunAltGrubu":
		return strings.TrimSpace(row.UrunAltGrubu)
	case "Icerik":
		return strings.TrimSpace(row.Icerik)
	case "Karisim":
		return strings.TrimSpace(row.Karisim)
	case "CostPrice":
		return fmtMoneyCell(row.CostPrice)
	case "BasePriceUsd":
		return fmtMoneyCell(row.BasePriceUsd)
	case "BasePriceTry":
		return fmtMoneyCell(row.BasePriceTry)
	case "USD1":
		return fmtMoneyCell(row.USD1)
	case "USD2":
		return fmtMoneyCell(row.USD2)
	case "USD3":
		return fmtMoneyCell(row.USD3)
	case "USD4":
		return fmtMoneyCell(row.USD4)
	case "USD5":
		return fmtMoneyCell(row.USD5)
	case "USD6":
		return fmtMoneyCell(row.USD6)
	case "EUR1":
		return fmtMoneyCell(row.EUR1)
	case "EUR2":
		return fmtMoneyCell(row.EUR2)
	case "EUR3":
		return fmtMoneyCell(row.EUR3)
	case "EUR4":
		return fmtMoneyCell(row.EUR4)
	case "EUR5":
		return fmtMoneyCell(row.EUR5)
	case "EUR6":
		return fmtMoneyCell(row.EUR6)
	case "TRY1":
		return fmtMoneyCell(row.TRY1)
	case "TRY2":
		return fmtMoneyCell(row.TRY2)
	case "TRY3":
		return fmtMoneyCell(row.TRY3)
	case "TRY4":
		return fmtMoneyCell(row.TRY4)
	case "TRY5":
		return fmtMoneyCell(row.TRY5)
	case "TRY6":
		return fmtMoneyCell(row.TRY6)
	default:
		return ""
	}
}

func ExportProductPriceListExcelHandler(pg *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		traceID := utils.TraceIDFromRequest(r)
		claims, ok := auth.GetClaimsFromContext(r.Context())
		if !ok || claims == nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}

		var req priceListExportRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			http.Error(w, "invalid payload", http.StatusBadRequest)
			return
		}
		if !req.IncludeMeta && !req.IncludeCost && !req.IncludeBase && len(req.USDLevels) == 0 && len(req.EURLevels) == 0 && len(req.TRYLevels) == 0 {
			req.IncludeMeta = true
			req.IncludeCost = true
			req.IncludeBase = true
			req.USDLevels = []int{1, 2, 3, 4, 5, 6}
			req.EURLevels = []int{1, 2, 3, 4, 5, 6}
			req.TRYLevels = []int{1, 2, 3, 4, 5, 6}
		}
		if req.IncludeMeta == false {
			req.IncludeMeta = true
		}

		ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 2*time.Minute)
		defer cancel()

		filters := queries.ProductPricingFilters{
			Search:       strings.TrimSpace(req.Search),
			ProductCode:  req.ProductCode,
			BrandGroup:   req.BrandGroup,
			AskiliYan:    req.AskiliYan,
			Kategori:     req.Kategori,
			UrunIlkGrubu: req.UrunIlkGrubu,
			UrunAnaGrubu: req.UrunAnaGrubu,
			UrunAltGrubu: req.UrunAltGrubu,
			Icerik:       req.Icerik,
			Karisim:      req.Karisim,
			Marka:        req.Marka,
		}

		rows, err := queries.GetAllProductPricingRows(ctx, 500, filters, "productCode", false)
		if err != nil {
			http.Error(w, "query error", http.StatusInternalServerError)
			return
		}
		if req.InStockOnly {
			tmp := make([]models.ProductPricing, 0, len(rows))
			for _, it := range rows {
				if it.StockQty > 0.0001 {
					tmp = append(tmp, it)
				}
			}
			rows = tmp
		}

		cols := resolvePriceListColumns(req)

		f := excelize.NewFile()
		defer func() { _ = f.Close() }()
		sheet := "Fiyat Listesi"
		f.SetSheetName("Sheet1", sheet)

		now := time.Now()
		title := "BAGGI - GUNCEL FIYAT LISTESI"
		dateLine := "Tarih: " + now.Format("02.01.2006")

		_ = f.SetCellValue(sheet, "A1", title)
		_ = f.SetCellValue(sheet, "A2", dateLine)
		_ = f.MergeCell(sheet, "A1", "H1")
		_ = f.MergeCell(sheet, "A2", "H2")

		// Try to add logo (best-effort).
		if logoPath, err := resolvePdfImagePath("Baggi-Tekstil-A.s-Logolu.jpeg"); err == nil {
			_ = f.AddPicture(sheet, "I1", logoPath, &excelize.GraphicOptions{
				ScaleX: 0.25,
				ScaleY: 0.25,
			})
		}

		// Header row
		headerRow := 4
		for i, c := range cols {
			cell, _ := excelize.CoordinatesToCellName(i+1, headerRow)
			_ = f.SetCellValue(sheet, cell, c.Title)
			colName, _ := excelize.ColumnNumberToName(i + 1)
			_ = f.SetColWidth(sheet, colName, colName, c.Width)
		}
		// Freeze panes at header
		_ = f.SetPanes(sheet, &excelize.Panes{
			Freeze:      true,
			Split:       false,
			XSplit:      0,
			YSplit:      headerRow,
			TopLeftCell: "A5",
			ActivePane:  "bottomLeft",
		})

		// Basic styles
		hStyle, _ := f.NewStyle(&excelize.Style{
			Font:      &excelize.Font{Bold: true, Color: "#FFFFFF"},
			Fill:      excelize.Fill{Type: "pattern", Color: []string{"#957116"}, Pattern: 1},
			Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
			Border: []excelize.Border{
				{Type: "left", Color: "#C0C0C0", Style: 1},
				{Type: "top", Color: "#C0C0C0", Style: 1},
				{Type: "bottom", Color: "#C0C0C0", Style: 1},
				{Type: "right", Color: "#C0C0C0", Style: 1},
			},
		})
		lastHeaderCell, _ := excelize.CoordinatesToCellName(len(cols), headerRow)
		_ = f.SetCellStyle(sheet, "A4", lastHeaderCell, hStyle)

		// Data rows
		startRow := headerRow + 1
		for ri, row := range rows {
			excelRow := startRow + ri
			for ci, c := range cols {
				cell, _ := excelize.CoordinatesToCellName(ci+1, excelRow)
				_ = f.SetCellValue(sheet, cell, getCellValue(row, c.Key))
			}
		}

		// Autofilter
		_ = f.AutoFilter(sheet, fmt.Sprintf("A4:%s", lastHeaderCell), []excelize.AutoFilterOptions{})

		var buf bytes.Buffer
		if err := f.Write(&buf); err != nil {
			http.Error(w, "excel write error", http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", fmt.Sprintf("baggi_guncel_fiyat_listesi_%s.xlsx", now.Format("20060102"))))
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(buf.Bytes())
	}
}

func ExportProductPriceListPDFHandler(pg *sql.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		traceID := utils.TraceIDFromRequest(r)
		claims, ok := auth.GetClaimsFromContext(r.Context())
		if !ok || claims == nil {
			http.Error(w, "unauthorized", http.StatusUnauthorized)
			return
		}
		var req priceListExportRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			http.Error(w, "invalid payload", http.StatusBadRequest)
			return
		}
		if !req.IncludeMeta && !req.IncludeCost && !req.IncludeBase && len(req.USDLevels) == 0 && len(req.EURLevels) == 0 && len(req.TRYLevels) == 0 {
			req.IncludeMeta = true
			req.IncludeCost = true
			req.IncludeBase = true
			req.USDLevels = []int{1, 2, 3, 4, 5, 6}
			req.EURLevels = []int{1, 2, 3, 4, 5, 6}
			req.TRYLevels = []int{1, 2, 3, 4, 5, 6}
		}
		req.IncludeMeta = true

		ctx, cancel := context.WithTimeout(utils.ContextWithTraceID(r.Context(), traceID), 2*time.Minute)
		defer cancel()

		filters := queries.ProductPricingFilters{
			Search:       strings.TrimSpace(req.Search),
			ProductCode:  req.ProductCode,
			BrandGroup:   req.BrandGroup,
			AskiliYan:    req.AskiliYan,
			Kategori:     req.Kategori,
			UrunIlkGrubu: req.UrunIlkGrubu,
			UrunAnaGrubu: req.UrunAnaGrubu,
			UrunAltGrubu: req.UrunAltGrubu,
			Icerik:       req.Icerik,
			Karisim:      req.Karisim,
			Marka:        req.Marka,
		}
		rows, err := queries.GetAllProductPricingRows(ctx, 500, filters, "productCode", false)
		if err != nil {
			http.Error(w, "query error", http.StatusInternalServerError)
			return
		}
		if req.InStockOnly {
			tmp := make([]models.ProductPricing, 0, len(rows))
			for _, it := range rows {
				if it.StockQty > 0.0001 {
					tmp = append(tmp, it)
				}
			}
			rows = tmp
		}

		cols := resolvePriceListColumns(req)

		pdf := gofpdf.New("L", "mm", "A4", "")
		pdf.SetMargins(8, 8, 8)
		pdf.SetAutoPageBreak(true, 10)
		_ = registerDejavuFonts(pdf, "dejavu")
		pdf.AddPage()

		// Header: logo + title + date
		y := 10.0
		if logoPath, err := resolvePdfImagePath("Baggi-Tekstil-A.s-Logolu.jpeg"); err == nil {
			pdf.ImageOptions(logoPath, 8, y-2, 26, 0, false, gofpdf.ImageOptions{}, 0, "")
		}
		pdf.SetFont("dejavu", "B", 14)
		pdf.SetTextColor(149, 113, 22)
		pdf.SetXY(36, y)
		pdf.CellFormat(0, 7, "BAGGI - GUNCEL FIYAT LISTESI", "", 0, "L", false, 0, "")
		pdf.SetTextColor(0, 0, 0)
		pdf.SetFont("dejavu", "", 9)
		pdf.SetXY(36, y+7)
		pdf.CellFormat(0, 5, "Tarih: "+time.Now().Format("02.01.2006"), "", 0, "L", false, 0, "")
		pdf.SetXY(36, y+12)
		pdf.CellFormat(0, 5, "Olusturan: "+strings.TrimSpace(claims.Username), "", 0, "L", false, 0, "")

		pdf.Ln(18)

		pageW, _ := pdf.GetPageSize()
		availW := pageW - 16
		sumW := 0.0
		for _, c := range cols {
			sumW += c.Width
		}
		scale := 1.0
		if sumW > 0 && sumW > availW {
			scale = availW / sumW
		}

		drawRow := func(isHeader bool, values []string) {
			h := 6.0
			if isHeader {
				pdf.SetFillColor(149, 113, 22)
				pdf.SetTextColor(255, 255, 255)
				pdf.SetFont("dejavu", "B", 7)
			} else {
				pdf.SetFillColor(255, 255, 255)
				pdf.SetTextColor(0, 0, 0)
				pdf.SetFont("dejavu", "", 7)
			}
			for i, c := range cols {
				w := c.Width * scale
				align := c.Align
				if align == "" {
					align = "L"
				}
				txt := ""
				if i < len(values) {
					txt = values[i]
				}
				pdf.CellFormat(w, h, txt, "1", 0, align, isHeader, 0, "")
			}
			pdf.Ln(-1)
		}

		// Header row
		headerVals := make([]string, 0, len(cols))
		for _, c := range cols {
			headerVals = append(headerVals, c.Title)
		}
		drawRow(true, headerVals)

		for _, row := range rows {
			vals := make([]string, 0, len(cols))
			for _, c := range cols {
				vals = append(vals, getCellValue(row, c.Key))
			}
			drawRow(false, vals)
		}

		var buf bytes.Buffer
		if err := pdf.Output(&buf); err != nil {
			http.Error(w, "pdf render error", http.StatusInternalServerError)
			return
		}

		now := time.Now()
		w.Header().Set("Content-Type", "application/pdf")
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", fmt.Sprintf("Fiyat_Listesi-%s.pdf", now.Format("20060102-1504"))))
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(buf.Bytes())
	}
}
