package routes

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"
	"log"
	"sort"
	"strings"
	"time"

	"bssapp-backend/db"
	"bssapp-backend/internal/mailer"
	"bssapp-backend/models"
	"bssapp-backend/queries"

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

func loadPricingRecipients(pg *sql.DB, firstGroupCode string) ([]string, error) {
	rows, err := pg.Query(`
SELECT DISTINCT TRIM(m.email) AS email
FROM mk_pricing_first_group_mail f
JOIN mk_mail m
  ON m.id = f.mail_id
WHERE m.is_active = true
  AND COALESCE(TRIM(m.email), '') <> ''
  AND UPPER(TRIM(f.urun_ilk_grubu)) = UPPER(TRIM($1))
ORDER BY email
`, strings.TrimSpace(firstGroupCode))
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]string, 0, 16)
	for rows.Next() {
		var email string
		if err := rows.Scan(&email); err != nil {
			return nil, err
		}
		email = strings.TrimSpace(email)
		if email != "" {
			out = append(out, email)
		}
	}
	return out, rows.Err()
}

func htmlEscapeMini(s string) string {
	// Minimal safe escaping for our templated cells.
	r := strings.NewReplacer(
		"&", "&amp;",
		"<", "&lt;",
		">", "&gt;",
		"\"", "&quot;",
		"'", "&#39;",
	)
	return r.Replace(s)
}

func fmtMoneyMail(v float64) string { return fmt.Sprintf("%.2f", v) }
func fmtQtyMail(v float64) string   { return fmt.Sprintf("%.2f", v) }

func fmtDateTRFromISO(d string) string {
	d = strings.TrimSpace(d)
	if len(d) >= 10 {
		d = d[:10]
	}
	parts := strings.Split(d, "-")
	if len(parts) != 3 {
		if d == "" {
			return "-"
		}
		return d
	}
	y, m, day := parts[0], parts[1], parts[2]
	if y == "" || m == "" || day == "" {
		return d
	}
	return day + "." + m + "." + y
}

func buildPricingChangeMailHTML(rows []models.ProductPricing, actor string, at time.Time) string {
	var b strings.Builder
	b.WriteString(`<div style="font-family:Segoe UI, Arial, sans-serif; font-size:18px; line-height:1.35; -webkit-text-size-adjust:100%;">`)
	b.WriteString(`<div style="margin-bottom:10px;">`)
	b.WriteString(`<div style="font-size:22px; margin-bottom:4px;"><b>Fiyat Degisikligi</b></div>`)
	if strings.TrimSpace(actor) != "" {
		b.WriteString(`<div>Islem Yapan: <b>` + htmlEscapeMini(actor) + `</b></div>`)
	}
	b.WriteString(`<div>Tarih: <b>` + htmlEscapeMini(at.Format("02.01.2006 15:04")) + `</b></div>`)
	b.WriteString(`<div>Urun Sayisi: <b>` + fmt.Sprintf("%d", len(rows)) + `</b></div>`)
	b.WriteString(`<div style="margin-top:10px;">Detaylar ekteki PDF dosyasindadir.</div>`)
	b.WriteString(`</div>`)
	b.WriteString(`</div>`)
	return b.String()
}

func buildPricingChangeMailPDF(rows []models.ProductPricing, actor string, at time.Time) ([]byte, error) {
	pdf := gofpdf.New("L", "mm", "A2", "")
	font := "Arial"
	if err := registerDejavuFonts(pdf, "dejavu"); err == nil {
		font = "dejavu"
	} else {
		log.Printf("[pricing-mail] pdf font fallback: %v", err)
	}
	pdf.SetMargins(7, 8, 7)
	pdf.SetAutoPageBreak(true, 10)
	pdf.AddPage()
	pdf.SetFont(font, "B", 13)
	pdf.CellFormat(0, 7, "Fiyat Degisikligi", "", 1, "L", false, 0, "")
	pdf.SetFont(font, "", 8)
	if strings.TrimSpace(actor) != "" {
		pdf.CellFormat(0, 5, "Islem Yapan: "+strings.TrimSpace(actor), "", 1, "L", false, 0, "")
	}
	pdf.CellFormat(0, 5, "Tarih: "+at.Format("02.01.2006 15:04"), "", 1, "L", false, 0, "")
	pdf.CellFormat(0, 5, fmt.Sprintf("Urun Sayisi: %d", len(rows)), "", 1, "L", false, 0, "")
	pdf.Ln(2)

	heads := []string{
		"MARKA GRUBU", "MARKA", "BRAND", "URUN KODU",
		"STOK", "STOK GIRIS", "SON MALIYET", "SON FIYAT",
		"ASKILI", "KATEGORI", "URUN ANA GRUBU", "URUN ALT GRUBU", "ICERIK", "KARISIM",
		"MALIYET", "TABAN USD", "TABAN TRY",
		"USD1", "USD2", "USD3", "USD4", "USD5", "USD6",
		"EUR1", "EUR2", "EUR3", "EUR4", "EUR5", "EUR6",
		"TRY1", "TRY2", "TRY3", "TRY4", "TRY5", "TRY6",
	}
	widths := []float64{
		20, 18, 18, 28,
		12, 18, 18, 18,
		14, 18, 22, 22, 20, 20,
		18, 18, 18,
		13, 13, 13, 13, 13, 13,
		13, 13, 13, 13, 13, 13,
		13, 13, 13, 13, 13, 13,
	}
	aligns := make([]string, len(heads))
	for i := range aligns {
		aligns[i] = "L"
	}
	for _, idx := range []int{4, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34} {
		aligns[idx] = "R"
	}
	writePDFTableHeader(pdf, font, heads, widths)

	pdf.SetFont(font, "", 5.7)
	for _, r := range rows {
		if pdf.GetY() > 405 {
			pdf.AddPage()
			writePDFTableHeader(pdf, font, heads, widths)
			pdf.SetFont(font, "", 5.7)
		}
		cells := []string{
			r.BrandGroupSec,
			r.Marka,
			r.BrandCode,
			r.ProductCode,
			fmtQtyMail(r.StockQty),
			fmtDateTRFromISO(r.StockEntryDate),
			fmtDateTRFromISO(r.LastCostingDate),
			fmtDateTRFromISO(r.LastPricingDate),
			r.AskiliYan,
			r.Kategori,
			r.UrunAnaGrubu,
			r.UrunAltGrubu,
			r.Icerik,
			r.Karisim,
			fmtMoneyMail(r.CostPrice),
			fmtMoneyMail(r.BasePriceUsd),
			fmtMoneyMail(r.BasePriceTry),
			fmtMoneyMail(r.USD1), fmtMoneyMail(r.USD2), fmtMoneyMail(r.USD3), fmtMoneyMail(r.USD4), fmtMoneyMail(r.USD5), fmtMoneyMail(r.USD6),
			fmtMoneyMail(r.EUR1), fmtMoneyMail(r.EUR2), fmtMoneyMail(r.EUR3), fmtMoneyMail(r.EUR4), fmtMoneyMail(r.EUR5), fmtMoneyMail(r.EUR6),
			fmtMoneyMail(r.TRY1), fmtMoneyMail(r.TRY2), fmtMoneyMail(r.TRY3), fmtMoneyMail(r.TRY4), fmtMoneyMail(r.TRY5), fmtMoneyMail(r.TRY6),
		}
		writePDFTableRow(pdf, cells, widths, aligns, 4.4)
	}

	var buf bytes.Buffer
	if err := pdf.Output(&buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// sendPricingChangeMails sends one mail per UrunIlkGrubu (group) based on mk_pricing_first_group_mail mapping.
// It is designed to be called post-commit in a goroutine.
func sendPricingChangeMails(bg context.Context, ml *mailer.GraphMailer, productCodes []string, actor string) {
	if ml == nil {
		return
	}
	pg := db.PgDB
	if pg == nil {
		log.Printf("[pricing-mail] skipped: pg not ready")
		return
	}
	// Ensure mapping tables exist.
	if err := ensureFirstGroupMailMappingTables(pg); err != nil {
		log.Printf("[pricing-mail] mapping bootstrap error: %v", err)
		return
	}

	ctx, cancel := context.WithTimeout(bg, 90*time.Second)
	defer cancel()

	codes := make([]string, 0, len(productCodes))
	seen := map[string]struct{}{}
	for _, c := range productCodes {
		c = strings.TrimSpace(c)
		if c == "" {
			continue
		}
		if _, ok := seen[c]; ok {
			continue
		}
		seen[c] = struct{}{}
		codes = append(codes, c)
	}
	if len(codes) == 0 {
		return
	}

	rows, err := queries.GetAllProductPricingRows(ctx, 500, queries.ProductPricingFilters{ProductCode: codes}, "productCode", false)
	if err != nil {
		log.Printf("[pricing-mail] pricing rows query error: %v", err)
		return
	}
	if len(rows) == 0 {
		return
	}

	byGroup := map[string][]models.ProductPricing{}
	for _, r := range rows {
		g := strings.TrimSpace(r.UrunIlkGrubu)
		if g == "" {
			g = "UNKNOWN"
		}
		byGroup[g] = append(byGroup[g], r)
	}

	now := time.Now()
	for group, list := range byGroup {
		// No mapping = skip.
		recipients, err := loadPricingRecipients(pg, group)
		if err != nil {
			log.Printf("[pricing-mail] recipient query error group=%s err=%v", group, err)
			continue
		}
		if len(recipients) == 0 {
			log.Printf("[pricing-mail] no recipients mapped group=%s", group)
			continue
		}

		sort.Slice(list, func(i, j int) bool {
			return strings.TrimSpace(list[i].ProductCode) < strings.TrimSpace(list[j].ProductCode)
		})

		subject := fmt.Sprintf("Fiyat Degisikligi | %s | %d urun", now.Format("02.01.2006 15:04"), len(list))
		html := buildPricingChangeMailHTML(list, actor, now)
		attachments := []mailer.Attachment(nil)
		if data, err := buildPricingChangeMailPDF(list, actor, now); err != nil {
			log.Printf("[pricing-mail] pdf build failed group=%s err=%v", group, err)
		} else {
			attachments = append(attachments, mailer.Attachment{
				FileName:    fmt.Sprintf("fiyat-degisikligi-%s.pdf", now.Format("20060102-1504")),
				ContentType: "application/pdf",
				Data:        data,
			})
		}

		// Retry 2 times with backoff.
		backoff := []time.Duration{800 * time.Millisecond, 2500 * time.Millisecond}
		var lastErr error
		for attempt := 0; attempt < len(backoff)+1; attempt++ {
			if attempt > 0 {
				time.Sleep(backoff[attempt-1])
			}
			stepCtx, stepCancel := context.WithTimeout(bg, 25*time.Second)
			err := ml.Send(stepCtx, mailer.Message{
				To:          recipients,
				Subject:     subject,
				BodyHTML:    html,
				Attachments: attachments,
			})
			stepCancel()
			if err == nil {
				lastErr = nil
				break
			}
			lastErr = err
		}
		if lastErr != nil {
			log.Printf("[pricing-mail] send failed group=%s err=%v", group, lastErr)
		} else {
			log.Printf("[pricing-mail] sent group=%s to=%d products=%d", group, len(recipients), len(list))
		}
	}
}
