// routes/statements_pdf.go
package routes

import (
	"bssapp-backend/auth"
	"bssapp-backend/internal/i18n"
	"bssapp-backend/models"
	"bssapp-backend/queries"
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"runtime/debug"
	"sort"
	"strings"
	"time"

	"github.com/jung-kurt/gofpdf"
)

type statementPDFHeaderRow struct {
	CariKod     string  `json:"cari_kod"`
	CariIsim    string  `json:"cari_isim"`
	BelgeTarihi string  `json:"belge_tarihi"`
	VadeTarihi  string  `json:"vade_tarihi"`
	BelgeNo     string  `json:"belge_no"`
	IslemTipi   string  `json:"islem_tipi"`
	Aciklama    string  `json:"aciklama"`
	ParaBirimi  string  `json:"para_birimi"`
	Borc        float64 `json:"borc"`
	Alacak      float64 `json:"alacak"`
	Bakiye      float64 `json:"bakiye"`
}

type statementPDFPayload struct {
	AccountCode string                  `json:"account_code"`
	StartDate   string                  `json:"start_date"`
	EndDate     string                  `json:"end_date"`
	LangCode    string                  `json:"lang_code"`
	Rows        []statementPDFHeaderRow `json:"rows"`
}

/* ============================ SABİTLER ============================ */

// A4 Landscape (mm)
const (
	pageWidth  = 297.0
	pageHeight = 210.0

	// Kenar boşlukları (mm)
	marginL = 8.0
	marginT = 8.0
	marginR = 8.0
	marginB = 15.0

	// Satır aralıkları (mm)
	lineHMain   = 6.0 // ana satır biraz iri
	lineHDetail = 5.2
	cellPadX    = 2.2

	// Satır yükseklikleri (mm)
	headerRowH    = 8.4
	subHeaderRowH = 6.2
	groupBarH     = 9.0

	// Çizgi kalınlığı
	gridLineWidth = 0.3

	// Logo genişliği (yükseklik oranlanır)
	logoW = 42.0
)

func mainCols(lang string) []string {
	return []string{
		i18n.T(lang, "pdf.main.doc_no"),
		i18n.T(lang, "pdf.main.date"),
		i18n.T(lang, "pdf.main.due_date"),
		i18n.T(lang, "pdf.main.operation"),
		i18n.T(lang, "pdf.main.description"),
		i18n.T(lang, "pdf.main.currency"),
		i18n.T(lang, "pdf.main.debit"),
		i18n.T(lang, "pdf.main.credit"),
		i18n.T(lang, "pdf.main.balance"),
	}
}

// Ana tablo kolon genişlikleri (ilk 3 geniş)
var mainWbase = []float64{
	34, // Belge No
	30, // Tarih
	28, // Vade
	24, // İşlem
	60, // Açıklama (biraz daraltıldı)
	20, // Para
	36, // Borç (genişletildi)
	36, // Alacak (genişletildi)
	36, // Bakiye (genişletildi)
}

// Detay tablo kolonları ve genişlikleri
func detailCols(lang string) []string {
	return []string{
		i18n.T(lang, "pdf.detail.main_group"),
		i18n.T(lang, "pdf.detail.sub_group"),
		i18n.T(lang, "pdf.detail.waiter"),
		i18n.T(lang, "pdf.detail.fit"),
		i18n.T(lang, "pdf.detail.content"),
		i18n.T(lang, "pdf.detail.product"),
		i18n.T(lang, "pdf.detail.color"),
		i18n.T(lang, "pdf.detail.qty"),
		i18n.T(lang, "pdf.detail.price"),
		i18n.T(lang, "pdf.detail.total"),
	}
}

var dWbase = []float64{
	30, 28, 22, 20, 56, 30, 22, 20, 20, 26}

// Font dosyaları
const (
	fontFamilyReg  = "dejavu"
	fontFamilyBold = "dejavu"
)

// Kurumsal renkler
var (
	colorPrimary    = [3]int{149, 113, 22}  // #957116 (altın)
	colorSecondary  = [3]int{218, 193, 151} // #dac197 (bej)
	colorDetailFill = [3]int{242, 235, 222} // secondary’den daha açık (detay satır zemin)
)

/* ============================ HELPERS ============================ */

// Genişlikleri normalize et
func normalizeWidths(base []float64, targetTotal float64) []float64 {
	sum := 0.0
	for _, v := range base {
		sum += v
	}
	out := make([]float64, len(base))
	if sum <= 0 {
		each := targetTotal / float64(len(base))
		for i := range out {
			out[i] = each
		}
		return out
	}
	scale := targetTotal / sum
	for i, v := range base {
		out[i] = v * scale
	}
	return out
}

// Türkçe formatlı sayı
func formatCurrencyTR(n float64) string {
	s := fmt.Sprintf("%.2f", n) // "864000.00"
	parts := strings.Split(s, ".")
	intPart := parts[0]
	decPart := parts[1]

	// Binlik ayırıcı (.)
	var out strings.Builder
	for i, c := range intPart {
		if (len(intPart)-i)%3 == 0 && i != 0 {
			out.WriteRune('.')
		}
		out.WriteRune(c)
	}

	return out.String() + "," + decPart
}

// Fontları yükle
func ensureFonts(pdf *gofpdf.Fpdf) error {
	return registerDejavuFonts(pdf, fontFamilyReg)
}

// Güvenli satır kırma
func splitLinesSafe(pdf *gofpdf.Fpdf, text string, width float64) [][]byte {
	if width <= 0 {
		width = 1
	}
	defer func() {
		if rec := recover(); rec != nil {
			log.Printf("⚠️ splitLinesSafe recover: %v", rec)
		}
	}()
	return pdf.SplitLines([]byte(text), width)
}

// Metin wrap çizimi
func drawWrapText(pdf *gofpdf.Fpdf, text string, x, y, width, lineH float64) {
	if text == "" {
		return
	}
	lines := splitLinesSafe(pdf, text, width)
	cy := y
	for _, ln := range lines {
		pdf.SetXY(x, cy)
		pdf.CellFormat(width, lineH, string(ln), "", 0, "L", false, 0, "")
		cy += lineH
	}
}

// Wrap satır yüksekliği
func calcRowHeightForText(pdf *gofpdf.Fpdf, text string, colWidth, lineHeight, padX float64) float64 {
	if colWidth <= 2*padX {
		colWidth = 2*padX + 1
	}
	if text == "" {
		return lineHeight
	}
	lines := splitLinesSafe(pdf, text, colWidth-(2*padX))
	if len(lines) == 0 {
		return lineHeight
	}
	h := float64(len(lines)) * lineHeight
	if h < lineHeight {
		h = lineHeight
	}
	return h
}

// Alt tarafa sığmıyor mu?
func needNewPage(pdf *gofpdf.Fpdf, needH float64) bool {
	return pdf.GetY()+needH+marginB > pageHeight
}

// NULL/boş’ları uzun çizgiye çevir
func nullToDash(s string) string {
	if strings.TrimSpace(s) == "" {
		return "—"
	}
	return s
}

/* ============================ HEADER DRAW ============================ */

// Küçük yardımcı: kutu içine otomatik 1-2 satır metin (taşarsa 2)
func drawLabeledBox(pdf *gofpdf.Fpdf, x, y, w, h float64, label, value string, align string) {
	// Çerçeve
	pdf.Rect(x, y, w, h, "")
	// İç marj
	ix := x + 2
	iy := y + 1.8
	iw := w - 4
	// Etiket (kalın, küçük)
	pdf.SetFont(fontFamilyBold, "", 8)
	pdf.SetXY(ix, iy)
	pdf.CellFormat(iw, 4, label, "", 0, "L", false, 0, "")

	// Değer (normal, 1-2 satır)
	pdf.SetFont(fontFamilyReg, "", 8)
	vy := iy + 4.2
	lineH := 4.2
	lines := splitLinesSafe(pdf, value, iw)
	if len(lines) > 2 {
		lines = lines[:2] // en fazla 2 satır göster
	}
	for _, ln := range lines {
		pdf.SetXY(ix, vy)
		pdf.CellFormat(iw, lineH, string(ln), "", 0, align, false, 0, "")
		vy += lineH
	}
}

func drawPageHeader(pdf *gofpdf.Fpdf, lang, cariKod, cariIsim, start, end string) float64 {
	if logoPath, err := resolvePdfImagePath("Baggi-Tekstil-A.s-Logolu.jpeg"); err == nil {
		pdf.ImageOptions(logoPath, hMarginL, 2, hLogoW, 0, false, gofpdf.ImageOptions{}, 0, "")
	}

	// Başlıklar
	pdf.SetFont(hFontFamilyBold, "", 16)
	pdf.SetTextColor(hColorPrimary[0], hColorPrimary[1], hColorPrimary[2])
	pdf.SetXY(hMarginL+hLogoW+8, hMarginT+2)
	pdf.CellFormat(120, 7, "Baggi Software System", "", 0, "L", false, 0, "")

	pdf.SetFont(hFontFamilyBold, "", 12)
	pdf.SetXY(hMarginL+hLogoW+8, hMarginT+10)
	pdf.CellFormat(120, 6, i18n.T(lang, "pdf.report_title"), "", 0, "L", false, 0, "")

	// Bugünün tarihi (sağ üst)
	today := time.Now().Format("02.01.2006")
	pdf.SetFont(hFontFamilyReg, "", 9)
	pdf.SetXY(hPageWidth-hMarginR-40, hMarginT+3)
	pdf.CellFormat(40, 6, i18n.T(lang, "pdf.date")+": "+today, "", 0, "R", false, 0, "")

	// Cari & Tarih kutuları (daha yukarı taşındı)
	boxY := hMarginT + hLogoW - 6
	pdf.SetFont(hFontFamilyBold, "", 10)

	pdf.Rect(hMarginL, boxY, 140, 11, "")
	pdf.SetXY(hMarginL+2, boxY+3)
	pdf.CellFormat(136, 5, fmt.Sprintf("%s: %s — %s", i18n.T(lang, "pdf.customer"), cariKod, cariIsim), "", 0, "L", false, 0, "")

	pdf.Rect(hPageWidth-hMarginR-140, boxY, 140, 11, "")
	pdf.SetXY(hPageWidth-hMarginR-138, boxY+3)
	pdf.CellFormat(136, 5, fmt.Sprintf("%s: %s → %s", i18n.T(lang, "pdf.date_range"), start, end), "", 0, "R", false, 0, "")

	// Alt çizgi
	y := boxY + 13
	pdf.SetDrawColor(hColorPrimary[0], hColorPrimary[1], hColorPrimary[2])
	pdf.Line(hMarginL, y, hPageWidth-hMarginR, y)
	pdf.SetDrawColor(200, 200, 200)

	return y + 4
}

/* ============================ GROUP BAR ============================ */

func drawGroupBar(pdf *gofpdf.Fpdf, lang, currency string, sonBakiye float64) {
	// Kutu alanı (tam genişlik)
	x := marginL
	y := pdf.GetY()
	w := pageWidth - marginL - marginR
	h := groupBarH

	// Çerçeve
	pdf.SetDrawColor(colorPrimary[0], colorPrimary[1], colorPrimary[2])
	pdf.SetLineWidth(0.6)
	pdf.Rect(x, y, w, h, "")

	// Metinler
	pdf.SetFont(fontFamilyBold, "", 11.3) // bir tık büyük
	pdf.SetTextColor(colorPrimary[0], colorPrimary[1], colorPrimary[2])

	pdf.SetXY(x+cellPadX+1.0, y+(h-5.0)/2)
	pdf.CellFormat(w*0.6, 5.0, fmt.Sprintf("%s: %s", i18n.T(lang, "pdf.currency_prefix"), currency), "", 0, "L", false, 0, "")

	txt := i18n.T(lang, "pdf.ending_balance") + " = " + formatCurrencyTR(sonBakiye)
	pdf.SetXY(x+w*0.4, y+(h-5.0)/2)
	pdf.CellFormat(w*0.6-2.0, 5.0, txt, "", 0, "R", false, 0, "")

	// Renk/kalınlık geri
	pdf.SetLineWidth(gridLineWidth)
	pdf.SetDrawColor(200, 200, 200)
	pdf.SetTextColor(0, 0, 0)

	pdf.Ln(h)
}

/* ============================ HEADER ROWS ============================ */

func drawMainHeaderRow(pdf *gofpdf.Fpdf, cols []string, widths []float64) {
	// Ana başlık: Primary arkaplan, beyaz yazı
	pdf.SetFont(fontFamilyBold, "", 10.2)
	pdf.SetTextColor(255, 255, 255)
	pdf.SetFillColor(colorPrimary[0], colorPrimary[1], colorPrimary[2])
	pdf.SetDrawColor(120, 90, 20)

	x := marginL
	y := pdf.GetY()
	for i, c := range cols {
		pdf.Rect(x, y, widths[i], headerRowH, "DF")
		pdf.SetXY(x+cellPadX, y+1.6)
		pdf.CellFormat(widths[i]-2*cellPadX, headerRowH-3.2, c, "", 0, "C", false, 0, "")
		x += widths[i]
	}
	pdf.SetY(y + headerRowH)

}

func drawDetailHeaderRow(pdf *gofpdf.Fpdf, cols []string, widths []float64) {
	// Detay başlık: Secondary (daha açık)
	pdf.SetFont(fontFamilyBold, "", 9.2)
	pdf.SetFillColor(colorSecondary[0], colorSecondary[1], colorSecondary[2])
	pdf.SetTextColor(0, 0, 0)
	pdf.SetDrawColor(160, 140, 100)

	x := marginL
	y := pdf.GetY()
	for i, c := range cols {
		pdf.Rect(x, y, widths[i], subHeaderRowH, "DF")
		pdf.SetXY(x+cellPadX, y+1.2)
		pdf.CellFormat(widths[i]-2*cellPadX, subHeaderRowH-2.4, c, "", 0, "C", false, 0, "")
		x += widths[i]
	}
	pdf.Ln(subHeaderRowH)
}

/* ============================ DATA ROWS ============================ */

// Ana veri satırı: daha büyük ve kalın görünsün
// Ana veri satırı (zebrasız, sıkı grid – satırlar yapışık)
func drawMainDataRow(pdf *gofpdf.Fpdf, row []string, widths []float64, rowH float64) {
	x := marginL
	y := pdf.GetY()

	// Arka plan beyaz, çizgiler gri
	pdf.SetFont(fontFamilyBold, "", 8.5)
	pdf.SetDrawColor(210, 210, 210)
	pdf.SetTextColor(30, 30, 30)
	pdf.SetFillColor(255, 255, 255)

	for i, val := range row {
		// Hücre çerçevesi
		pdf.Rect(x, y, widths[i], rowH, "")
		switch {
		case i == 4: // Açıklama wrap
			drawWrapText(pdf, val, x+cellPadX, y+0.8, widths[i]-2*cellPadX, lineHMain)
		case i >= 6: // Sayısal sağ
			pdf.SetXY(x+cellPadX, y+(rowH-lineHMain)/2)
			pdf.CellFormat(widths[i]-2*cellPadX, lineHMain, val, "", 0, "R", false, 0, "")
		default: // Normal sol
			pdf.SetXY(x+cellPadX, y+(rowH-lineHMain)/2)
			pdf.CellFormat(widths[i]-2*cellPadX, lineHMain, val, "", 0, "L", false, 0, "")
		}
		x += widths[i]
	}

	// ❌ pdf.Ln(rowH) yerine:
	// ✅ bir alt satıra tam yapışık in
	pdf.SetY(y + rowH)
}

// Detay veri satırı: açık zemin, biraz küçük; zebra opsiyonel
func drawDetailDataRow(pdf *gofpdf.Fpdf, row []string, widths []float64, rowH float64, fillBg bool) {
	pdf.SetFont(fontFamilyReg, "", 8.0)
	pdf.SetTextColor(60, 60, 60)
	pdf.SetDrawColor(220, 220, 220)

	x := marginL
	y := pdf.GetY()

	for i, val := range row {
		// Zemin
		if fillBg {
			pdf.SetFillColor(colorDetailFill[0], colorDetailFill[1], colorDetailFill[2])
			pdf.Rect(x, y, widths[i], rowH, "DF")
			pdf.SetFillColor(255, 255, 255) // geri
		} else {
			pdf.Rect(x, y, widths[i], rowH, "")
		}

		switch {
		case i == 4: // İçerik wrap
			drawWrapText(pdf, val, x+cellPadX, y+1.4, widths[i]-2*cellPadX, lineHDetail)
		case i >= 7: // Sayısal sağ
			pdf.SetXY(x+cellPadX, y+(rowH-lineHDetail)/2)
			pdf.CellFormat(widths[i]-2*cellPadX, lineHDetail, val, "", 0, "R", false, 0, "")
		default: // Sol
			pdf.SetXY(x+cellPadX, y+(rowH-lineHDetail)/2)
			pdf.CellFormat(widths[i]-2*cellPadX, lineHDetail, val, "", 0, "L", false, 0, "")
		}
		x += widths[i]
	}
	pdf.Ln(rowH)
}

/* ============================ HANDLER ============================ */

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

		started := time.Now()

		defer func() {
			if rec := recover(); rec != nil {
				log.Printf("❌ PANIC ExportPDFHandler: %v", rec)
				debug.PrintStack()
				http.Error(w, fmt.Sprintf("PDF oluşturulurken panic oluştu: %v", rec), http.StatusInternalServerError)
			}
		}()

		accountCode := r.URL.Query().Get("accountcode")
		startDate := r.URL.Query().Get("startdate")
		endDate := r.URL.Query().Get("enddate")
		langCode := i18n.ResolveLangCode(
			r.URL.Query().Get("langcode"),
			r.Header.Get("Accept-Language"),
		)

		// parislemler sanitize
		rawParis := r.URL.Query()["parislemler"]
		parislemler := make([]string, 0, len(rawParis))
		for _, v := range rawParis {
			v = strings.TrimSpace(v)
			if v == "" || strings.EqualFold(v, "undefined") || strings.EqualFold(v, "null") {
				continue
			}
			parislemler = append(parislemler, v)
		}
		log.Printf("▶️ ExportPDFHandler: account=%s start=%s end=%s parislemler=%v",
			accountCode, startDate, endDate, parislemler)

		var (
			headers  []models.StatementHeader
			belgeNos []string
			err      error
		)

		if strings.EqualFold(r.Method, http.MethodPost) {
			accountCode, startDate, endDate, langCode, headers, err = parseStatementPDFPayload(r, langCode)
			if err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			belgeNos = queriesCollectBelgeNos(headers)
		} else {
			headers, belgeNos, err = queries.GetStatementsPDF(r.Context(), accountCode, startDate, endDate, langCode, parislemler)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
		log.Printf("✅ Header verileri alındı: %d kayıt, %d belge no", len(headers), len(belgeNos))

		// 2) Detay verileri
		detailMap, err := queries.GetDetailsMapPDF(belgeNos, startDate, endDate)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		log.Printf("✅ Detay verileri alındı: %d belge için detay var", len(detailMap))

		// 3) Gruplama
		type grp struct {
			code      string
			rows      []models.StatementHeader
			sonBakiye float64
		}
		order := []string{}
		groups := map[string]*grp{}

		for _, h := range headers {
			g, ok := groups[h.ParaBirimi]
			if !ok {
				g = &grp{code: h.ParaBirimi}
				groups[h.ParaBirimi] = g
				order = append(order, h.ParaBirimi)
			}
			g.rows = append(g.rows, h)
		}

		for _, k := range order {
			sort.SliceStable(groups[k].rows, func(i, j int) bool {
				ri, rj := groups[k].rows[i], groups[k].rows[j]
				return parseStatementHeaderDate(ri.BelgeTarihi).Before(parseStatementHeaderDate(rj.BelgeTarihi))
			})
			if n := len(groups[k].rows); n > 0 {
				groups[k].sonBakiye = groups[k].rows[n-1].Bakiye
			}
		}

		// 4) Kolon genişlikleri
		wAvail := pageWidth - marginL - marginR
		mainWn := normalizeWidths(mainWbase, wAvail)
		dWn := normalizeWidths(dWbase, wAvail)

		// 5) PDF init
		pdf := gofpdf.New("L", "mm", "A4", "")
		pdf.SetMargins(marginL, marginT, marginR)
		pdf.SetAutoPageBreak(false, marginB)
		if err := ensureFonts(pdf); err != nil {
			http.Error(w, "PDF font yükleme hatası: "+err.Error(), http.StatusInternalServerError)
			return
		}
		pdf.SetFont(fontFamilyReg, "", 8.5)
		pdf.SetTextColor(0, 0, 0)

		pageNum := 0
		cariIsim := ""
		if len(headers) > 0 {
			cariIsim = headers[0].CariIsim
		}

		// Sayfa başlatıcı (header yüksekliği dinamik)
		newPage := func() {
			pageNum++
			pdf.AddPage()

			// drawPageHeader tablo başlangıç yüksekliğini döndürüyor
			tableTop := drawPageHeader(pdf, langCode, accountCode, cariIsim, startDate, endDate)

			// Sayfa numarası
			pdf.SetFont(fontFamilyReg, "", 6)
			pdf.SetXY(pageWidth-marginR-28, pageHeight-marginB+3)
			pdf.CellFormat(28, 5, fmt.Sprintf("%s %d", i18n.T(langCode, "pdf.page"), pageNum), "", 0, "R", false, 0, "")

			// Tablo Y konumunu ayarla
			pdf.SetY(tableTop)
		}

		newPage()

		// 6) Gruplar yaz
		for _, cur := range order {
			g := groups[cur]

			if needNewPage(pdf, groupBarH+headerRowH) {
				newPage()
			}
			drawGroupBar(pdf, langCode, cur, g.sonBakiye)
			drawMainHeaderRow(pdf, mainCols(langCode), mainWn)

			for _, h := range g.rows {
				row := []string{
					h.BelgeNo, h.BelgeTarihi, h.VadeTarihi, h.IslemTipi,
					h.Aciklama, h.ParaBirimi,
					formatCurrencyTR(h.Borc),
					formatCurrencyTR(h.Alacak),
					formatCurrencyTR(h.Bakiye),
				}

				pdf.SetFont(fontFamilyBold, "", 9.6)
				rh := calcRowHeightForText(pdf, row[4], mainWn[4], lineHMain, cellPadX)

				if needNewPage(pdf, rh+headerRowH) {
					newPage()
					drawGroupBar(pdf, langCode, cur, g.sonBakiye)
					drawMainHeaderRow(pdf, mainCols(langCode), mainWn)
				}
				drawMainDataRow(pdf, row, mainWn, rh)

				// detaylar
				details := detailMap[h.BelgeNo]
				if len(details) > 0 {
					if needNewPage(pdf, subHeaderRowH) {
						newPage()
						drawGroupBar(pdf, langCode, cur, g.sonBakiye)
						drawMainHeaderRow(pdf, mainCols(langCode), mainWn)
					}
					drawDetailHeaderRow(pdf, detailCols(langCode), dWn)

					for i, d := range details {
						drow := []string{
							nullToDash(d.UrunAnaGrubu),
							nullToDash(d.UrunAltGrubu),
							nullToDash(d.YetiskinGarson),
							nullToDash(d.Fit),
							nullToDash(d.Icerik),
							nullToDash(d.UrunKodu),
							nullToDash(d.UrunRengi),
							formatCurrencyTR(d.ToplamAdet),
							formatCurrencyTR(d.ToplamFiyat),
							formatCurrencyTR(d.ToplamTutar),
						}

						pdf.SetFont(fontFamilyReg, "", 8)
						rh2 := calcRowHeightForText(pdf, drow[4], dWn[4], lineHDetail, cellPadX)

						if needNewPage(pdf, rh2) {
							newPage()
							drawGroupBar(pdf, langCode, cur, g.sonBakiye)
							drawMainHeaderRow(pdf, mainCols(langCode), mainWn)
							drawDetailHeaderRow(pdf, detailCols(langCode), dWn)
						}
						// zebra: çift indekslerde açık zemin
						fill := (i%2 == 0)
						drawDetailDataRow(pdf, drow, dWn, rh2, fill)
					}
				}
			}
			pdf.Ln(3)
		}

		// 7) Çıktı
		if err := pdf.Error(); err != nil {
			http.Error(w, "PDF render hatası: "+err.Error(), http.StatusInternalServerError)
			return
		}

		var buf bytes.Buffer
		if err := pdf.Output(&buf); err != nil {
			http.Error(w, "PDF oluşturulamadı: "+err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/pdf")
		w.Header().Set("Content-Disposition", "inline; filename=statement.pdf")
		_, _ = w.Write(buf.Bytes())

		log.Printf("✅ PDF üretimi tamam: %s", time.Since(started))
	}
}

func parseStatementPDFPayload(r *http.Request, fallbackLang string) (string, string, string, string, []models.StatementHeader, error) {
	var payload statementPDFPayload
	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
		return "", "", "", "", nil, fmt.Errorf("invalid statement pdf payload: %w", err)
	}

	langCode := i18n.ResolveLangCode(payload.LangCode, fallbackLang)
	headers := make([]models.StatementHeader, 0, len(payload.Rows))
	for _, row := range payload.Rows {
		headers = append(headers, models.StatementHeader{
			CariKod:     row.CariKod,
			CariIsim:    row.CariIsim,
			BelgeTarihi: row.BelgeTarihi,
			VadeTarihi:  row.VadeTarihi,
			BelgeNo:     row.BelgeNo,
			IslemTipi:   row.IslemTipi,
			Aciklama:    row.Aciklama,
			ParaBirimi:  row.ParaBirimi,
			Borc:        row.Borc,
			Alacak:      row.Alacak,
			Bakiye:      row.Bakiye,
		})
	}

	return payload.AccountCode, payload.StartDate, payload.EndDate, langCode, headers, nil
}

func parseStatementHeaderDate(value string) time.Time {
	value = strings.TrimSpace(value)
	if value == "" {
		return time.Time{}
	}
	if t, err := time.Parse("2006-01-02", value); err == nil {
		return t
	}
	if t, err := time.Parse(time.RFC3339, value); err == nil {
		return t
	}
	return time.Time{}
}

func queriesCollectBelgeNos(headers []models.StatementHeader) []string {
	seen := make(map[string]struct{}, len(headers))
	out := make([]string, 0, len(headers))
	for _, h := range headers {
		no := strings.TrimSpace(h.BelgeNo)
		if no == "" || no == "Baslangic_devir" {
			continue
		}
		if _, ok := seen[no]; ok {
			continue
		}
		seen[no] = struct{}{}
		out = append(out, no)
	}
	return out
}

/*
NOTLAR:
- Header artık dinamik yüksekliğe sahip (drawPageHeader -> contentTopY döner).
- Logo sol üst köşe, başlık “toolbar” gibi; “Cari / Tarih” kutuları
  1–2 satır metni çerçeve içinde otomatik kırar.
- Grup barı primary renkte çerçeveli ve yazı biraz büyük.
- Ana satırlar kalın & bir tık büyük (detaydan farklı).
- Detay satırlar açık (bej) zeminle zebra (i%2==0) uygulanır.
- SplitLines her yerde splitLinesSafe ile korunuyor (panic yok).
- AddPage sadece newPage()’de.
- Font bulunamazsa gömme fontlarla devam edilir (log yazar).
*/
