package genpdf

import (
	excelread "business-engine/internal/excel_read"
	"fmt"
	"slices"
	"time"

	"codeberg.org/go-pdf/fpdf"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

// LineItem repräsentiert eine einzelne Position in der Abrechnung.
type LineItem struct {
	Lohnart      string
	Beschreibung string
	Anzahl       float64
	Betrag       float64
	Faktor       float64
}

type StatementInfo struct {
	GearbeiteteStunden float64
	GearbeiteteTage    float64
	Urlaubsgeld        float64
	Krankheitstage     float64
}

// Total berechnet den Gesamtwert der Position.
func (item LineItem) Total() float64 {
	if item.Faktor > 0 {
		return item.Anzahl * item.Betrag * (item.Faktor / 100)
	}
	return item.Anzahl * item.Betrag
}

func GenerateSalaryStatement(stmt excelread.PayrollStatement, filename string, month time.Time) error {
	pdf := fpdf.New("P", "mm", "A4", "")
	pdf.AddUTF8Font("DejaVu", "", "DejaVuSans.ttf")
	pdf.AddUTF8Font("DejaVu", "B", "DejaVuSans-Bold.ttf")
	pdf.AddPage()

	pdf.SetFont("DejaVu", "", 12)
	pdf.Cell(0, 10, month.Format("01-2006"))
	pdf.Ln(12)

	pdf.SetDrawColor(200, 200, 200)
	pdf.SetLineWidth(0.5)

	x, y := pdf.GetXY()
	pdf.Line(x, y, x+40, y)
	pdf.Ln(10)

	pdf.SetFont("DejaVu", "B", 16)
	title := fmt.Sprintf("Gehaltsaufstellung für %s %s", stmt.Vorname, stmt.Nachname)
	pdf.Cell(0, 10, title)
	pdf.Ln(15)

	drawInfoBlock(pdf, StatementInfo{
		GearbeiteteStunden: stmt.GearbeiteteStunden,
		GearbeiteteTage:    stmt.GearbeiteteTage,
		Urlaubsgeld:        stmt.Urlaubsgeld,
		Krankheitstage:     stmt.Krankheitstage,
	})

	pdf.Ln(15)
	drawHeader(pdf)

	items := genLineItemsFromStatement(stmt)

	var gesamtbetrag float64
	for _, item := range items {
		drawRow(pdf, item)
		gesamtbetrag += item.Total()
	}

	drawSummary(pdf, gesamtbetrag)

	if err := pdf.OutputFileAndClose(filename); err != nil {
		return err
	}
	if pdf.Err() {
		return pdf.Error()
	}
	return nil
}

func genLineItemsFromStatement(stmt excelread.PayrollStatement) []LineItem {
	var items []LineItem

	// --- Gehalt & Stunden ---
	if stmt.Vertragsstunden > 0 && stmt.VertragStundenlohn > 0 {
		items = append(items, LineItem{
			Lohnart:      "0005",
			Beschreibung: "Gehalt",
			Anzahl:       stmt.Vertragsstunden,
			Betrag:       stmt.VertragStundenlohn,
		})
	}
	if stmt.Ueberstunden > 0 && stmt.VertragStundenlohn > 0 {
		items = append(items, LineItem{
			Lohnart:      "1250",
			Beschreibung: "Überstundenvergütung",
			Anzahl:       stmt.Ueberstunden,
			Betrag:       stmt.VertragStundenlohn,
		})
	}

	if stmt.Verpflegungspauschale > 0 {
		items = append(items, LineItem{
			Lohnart:      "0227",
			Beschreibung: "Verpflegungsmehrauf. 14€",
			Anzahl:       stmt.Verpflegungspauschale,
			Betrag:       14.00,
		})
	}
	if stmt.Urlaubsgeld > 0 {
		items = append(items, LineItem{
			Lohnart:      "0052",
			Beschreibung: "Urlaubsgeld Angestellte",
			Anzahl:       stmt.Urlaubsgeld,
			Betrag:       44.00,
		})
	}
	if stmt.TourengeldBonus > 0 {
		items = append(items, LineItem{
			Lohnart:      "5019",
			Beschreibung: "Bonus Tourengeld",
			Anzahl:       1,
			Betrag:       stmt.TourengeldBonus,
		})
	}
	if stmt.Tourengeld > 0 {
		items = append(items, LineItem{
			Lohnart:      "1030",
			Beschreibung: "Tourengeld",
			Anzahl:       stmt.Tourengeld,
			Betrag:       2.5,
		})
	}
	if stmt.PuenktlichkeitspraemieAnzahl > 0 {
		items = append(items, LineItem{
			Lohnart:      "0109",
			Beschreibung: "Verpflegung 25% (Pünktlichkeit)",
			Anzahl:       stmt.PuenktlichkeitspraemieAnzahl,
			Betrag:       14.0,
		})
	}
	if stmt.GesundheitsbonusAbzug == "" {
		items = append(items, LineItem{
			Lohnart:      "1032",
			Beschreibung: "Gesundheitsbonus",
			Anzahl:       1,
			Betrag:       200.0,
		})
	}

	if stmt.PScheinZulage > 0 {
		items = append(items, LineItem{
			Lohnart:      "1024",
			Beschreibung: "Zulage Personenbeförd.",
			Anzahl:       stmt.PScheinZulage,
			Betrag:       5.0,
		})
	}
	if stmt.Wochenendzuschlag15 > 0 {
		items = append(items, LineItem{
			Lohnart:      "1063",
			Beschreibung: "Samstagszuschlag 15%",
			Anzahl:       stmt.Wochenendzuschlag15,
			Betrag:       stmt.VertragStundenlohn,
			Faktor:       15,
		})
	}
	if stmt.Sonntagszuschlag50 > 0 {
		items = append(items, LineItem{
			Lohnart:      "0581",
			Beschreibung: "Sonntagszuschlag 50%",
			Anzahl:       stmt.Sonntagszuschlag50,
			Betrag:       stmt.VertragStundenlohn,
			Faktor:       50,
		})
	}
	if stmt.Feiertagszuschlag125 > 0 {
		items = append(items, LineItem{
			Lohnart:      "0551",
			Beschreibung: "Feiertagszuschlag 125%",
			Anzahl:       stmt.Feiertagszuschlag125,
			Betrag:       stmt.VertragStundenlohn,
			Faktor:       125,
		})
	}
	if stmt.Nachtzuschlag25 > 0 {
		items = append(items, LineItem{
			Lohnart:      "0505",
			Beschreibung: "Nachtzuschl. 20:00-24:00h",
			Anzahl:       stmt.Nachtzuschlag25,
			Betrag:       stmt.VertragStundenlohn,
			Faktor:       25,
		})
	}
	if stmt.Nachtzuschlag40 > 0 {
		items = append(items, LineItem{
			Lohnart:      "0501",
			Beschreibung: "Nachtzuschl. 0:00-4:00h",
			Anzahl:       stmt.Nachtzuschlag40,
			Betrag:       stmt.VertragStundenlohn,
			Faktor:       40,
		})
	}

	slices.SortFunc(items, func(a, b LineItem) int {
		return int(b.Total() - a.Total())
	})

	return items
}

func drawInfoBlock(pdf *fpdf.Fpdf, info StatementInfo) {
	const padding = 3.0
	const elementHeight = 4.0 + 6.0

	x, y := pdf.GetXY()
	drawLabelWithValue(pdf, x+padding, y, "Anw.-Std.", formatFloat(info.GearbeiteteStunden))
	drawLabelWithValue(pdf, x+padding+18, y, "Anw.-Tage.", formatFloat(info.GearbeiteteTage))
	pdf.Line(x+padding+38, y-padding, x+padding+38, y+elementHeight+padding)
	drawLabelWithValue(pdf, x+padding+40, y, "Urlaub", formatFloat(float64(info.Urlaubsgeld)))
	drawLabelWithValue(pdf, x+padding+55, y, "Krank.-Tage", formatFloat(float64(info.Krankheitstage)))

	rectX := x
	rectY := y - padding
	rectWidth := 4*20 + 2*padding - 5
	rectHeight := elementHeight + 2*padding
	pdf.SetFillColor(180, 200, 220)
	pdf.Rect(rectX, rectY, rectWidth, rectHeight, "D")
}

func drawLabelWithValue(pdf *fpdf.Fpdf, x, y float64, label, value string) {
	const labelHeight = 4.0
	const valueheight = 6.0

	pdf.SetFont("DejaVu", "", 8)
	pdf.SetTextColor(80, 100, 120)
	pdf.SetXY(x, y)
	pdf.Cell(20, labelHeight, label)

	pdf.SetFont("DejaVu", "", 10)
	pdf.SetTextColor(0, 0, 0)
	pdf.SetXY(x, y+labelHeight)
	pdf.Cell(20, valueheight, value)
}

func drawHeader(pdf *fpdf.Fpdf) {
	pdf.SetFont("DejaVu", "B", 10)
	pdf.SetFillColor(240, 240, 240)
	pdf.SetX(10)
	pdf.CellFormat(20, 7, "Lohnart", "1", 0, "C", true, 0, "")
	pdf.SetX(30)
	pdf.CellFormat(70, 7, "Bezeichnung", "1", 0, "L", true, 0, "")
	pdf.SetX(100)
	pdf.CellFormat(25, 7, "Menge", "1", 0, "R", true, 0, "")
	pdf.SetX(125)
	pdf.CellFormat(25, 7, "%-Zuschlag", "1", 0, "R", true, 0, "")
	pdf.SetX(150)
	pdf.CellFormat(25, 7, "Betrag", "1", 0, "R", true, 0, "")
	pdf.SetX(175)
	pdf.CellFormat(30, 7, "Gesamt", "1", 0, "R", true, 0, "")
	pdf.Ln(7)
}

func drawRow(pdf *fpdf.Fpdf, item LineItem) {
	pdf.SetFont("DejaVu", "", 10)
	pdf.SetX(10)
	pdf.CellFormat(20, 7, item.Lohnart, "1", 0, "C", false, 0, "")
	pdf.SetX(30)
	pdf.CellFormat(70, 7, item.Beschreibung, "1", 0, "L", false, 0, "")
	pdf.SetX(100)
	pdf.CellFormat(25, 7, formatFloat(item.Anzahl), "1", 0, "R", false, 0, "")
	pdf.SetX(125)
	pdf.CellFormat(25, 7, formatFloat(item.Faktor), "1", 0, "R", false, 0, "")
	pdf.SetX(150)
	pdf.CellFormat(25, 7, formatCurrency(item.Betrag), "1", 0, "R", false, 0, "")
	pdf.SetX(175)
	pdf.CellFormat(30, 7, formatCurrency(item.Total()), "1", 0, "R", false, 0, "")
	pdf.Ln(7)
}

func drawSummary(pdf *fpdf.Fpdf, total float64) {
	pdf.SetFont("DejaVu", "B", 11)
	pdf.SetY(pdf.GetY() + 5) // Etwas Abstand
	pdf.SetX(120)
	pdf.CellFormat(50, 8, "Gesamtbetrag:", "T", 0, "L", false, 0, "")
	pdf.SetX(170)
	pdf.CellFormat(30, 8, formatCurrency(total), "T", 0, "R", false, 0, "")
}

func formatFloat(f float64) string {
	p := message.NewPrinter(language.German)
	str := p.Sprintf("%.2f", f)
	return str //strings.Replace(str, ".", ",", 1)
}

func formatCurrency(val float64) string {
	return formatFloat(val) + "€"
}
