package excelread

import (
	"business-engine/internal/database"
	"fmt"
	"os"
	"slices"
	"strings"
	"time"

	"github.com/gocarina/gocsv"
)

func sortSliceByDate[T any](slice []T, getDate func(T) time.Time) {
	compare := func(a, b T) int {
		aDate := getDate(a)
		bDate := getDate(b)

		if aDate.Before(bDate) {
			return -1
		}
		if aDate.After(bDate) {
			return 1
		}
		return 0
	}
	slices.SortFunc(slice, compare)
}

// Zu kalkulieren
// - Puenktlichkeitspraemie
// - Tourengeld Anzahl
// - Tourengeld Bonus
func GeneratePayrollStatement(data EmployeeDataRow, input PayrollInputData) PayrollStatement {
	isKTW := slices.IndexFunc(input.Plannedtimes, func(pt database.ListPlannedTimesForEmployeeInPeriodRow) bool {
		return strings.Contains(pt.Department.String, "KTW")
	}) >= 0 && slices.IndexFunc(input.Plannedtimes, func(pt database.ListPlannedTimesForEmployeeInPeriodRow) bool {
		return strings.Contains(pt.Department.String, "Leitstelle")
	}) == -1

	statement := PayrollStatement{
		Nachname:              data.Nachname,
		Vorname:               data.Vorname,
		Verpflegungspauschale: data.AuszahlenVerpflegungspauschaleTage,
		VertragStundenlohn:    input.ContractSalaryHour,
		Vertragsstunden:       data.IstSollVertragsStunden,
		GearbeiteteStunden:    data.GearbeiteteStunden,
		GearbeiteteTage:       data.GearbeiteteTage,
		Krankheitstage:        data.Krankheitstage,
		Ueberstunden:          data.IstSollPlusMinusStunden,
		Wochenendzuschlag15:   data.AuszahlenZuschlag15Stunden,
		Sonntagszuschlag50:    data.AuszahlenZuschlag50Stunden,
		Nachtzuschlag25:       data.AuszahlenZuschlag25Stunden,
		Nachtzuschlag40:       data.AuszahlenZuschlag40Stunden,
	}

	if data.Krankheitstage > 0 || !isKTW {
		statement.GesundheitsbonusAbzug = "ja"
	}

	if isKTW && data.GenommeneUrlaubstage > 0 {
		statement.Urlaubsgeld = data.GenommeneUrlaubstage
	}

	if isKTW && data.AuszahlenAnzahlPSchein > 0 {
		statement.PScheinZulage = data.AuszahlenAnzahlPSchein
	}

	if isKTW && input.Tourengeld > 0 {
		statement.Tourengeld = float64(input.Tourengeld)
	}

	if isKTW && len(input.Worktimes) > 0 && len(input.Plannedtimes) > 0 {
		ptGetStartDate := func(pt database.ListPlannedTimesForEmployeeInPeriodRow) time.Time {
			return *pt.StartDate
		}
		wtGetStartDate := func(pt database.WorkTime) time.Time {
			return *pt.StartDate
		}
		sortSliceByDate(input.Plannedtimes, ptGetStartDate)
		sortSliceByDate(input.Worktimes, wtGetStartDate)

		var employeeOnTime []EmployeeOnTime
		for _, pt := range input.Plannedtimes {
			for _, wt := range input.Worktimes {
				if isSameDay(ptGetStartDate(pt), wtGetStartDate(wt)) && (wtGetStartDate(wt).Before(ptGetStartDate(pt)) || wtGetStartDate(wt) == ptGetStartDate(pt)) {
					statement.PuenktlichkeitspraemieAnzahl++

					employeeOnTime = append(employeeOnTime, EmployeeOnTime{
						EmployeeId:  fmt.Sprintf("%s %s", data.Vorname, data.Nachname),
						WorkTime:    *wt.StartDate,
						PlannedTime: *pt.StartDate,
						IsOnTime:    true,
					})
				}
			}
		}
		if data.Vorname == "David" && data.Nachname == "Hastedt" {
			GenerateEmployeesOnTime(employeeOnTime)
		}
	}

	return statement
}

func GenerateEmployeesOnTime(employeesontime []EmployeeOnTime) error {
	file, err := os.Create("employees_on_time.csv")
	if err != nil {
		return err
	}
	defer file.Close()

	if err := gocsv.MarshalFile(&employeesontime, file); err != nil {
		return err
	}
	return nil
}

// isSameDay prüft, ob zwei Zeitpunkte am selben Kalendertag liegen.
func isSameDay(t1, t2 time.Time) bool {
	return t1.Year() == t2.Year() && t1.Month() == t2.Month() && t1.Day() == t2.Day()
}
