package main

import (
	"fmt"
	"os"
	"os/exec"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/xuri/excelize/v2"
)

type TimeWarrior struct{}

func NewTimeWarrior() *TimeWarrior {
	return &TimeWarrior{}
}

func (t *TimeWarrior) runCommand(args ...string) error {
	cmd := exec.Command("timew", args...)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func (t *TimeWarrior) StartWork() error {
	return t.runCommand("start", "work")
}

func (t *TimeWarrior) StopWork() error {
	return t.runCommand("stop", "work")
}

func (t *TimeWarrior) StartBreak() error {
	return t.runCommand("track", "break")
}

func (t *TimeWarrior) StopBreak() error {
	return t.runCommand("track", "work")
}

func (t *TimeWarrior) ShowSummary(period string) error {
	return t.runCommand("summary", period, "work")
}

type TimeEntry struct {
	Week     string
	Date     string
	Day      string
	Start    string
	End      string
	Duration string
	Tag      string
}

func (t *TimeWarrior) ExportSummary(name string) error {
	fmt.Println("Export Timetable")
	exportCommand := exec.Command("timew", "summary", ":year")
	output, err := exportCommand.Output()
	if err != nil {
		return err
	}
	lines := strings.Split(string(output), "\n")
	if len(lines) > 2 {
		var entries []TimeEntry
		for _, line := range lines[3 : len(lines)-4] {
			words := strings.Fields(line)
			newLine := strings.Join(words, " ")
			parts := strings.Split(strings.TrimSpace(newLine), " ")
			entry := TimeEntry{}

			switch len(parts) {
			case 4, 5:
				entry.Tag = parts[0]
				entry.Start = parts[1]
				entry.End = parts[2]
				entry.Duration = parts[3]
			case 7, 8:
				entry.Week = parts[0]
				entry.Date = parts[1]
				entry.Day = parts[2]
				entry.Tag = parts[3]
				entry.Start = parts[4]
				entry.End = parts[5]
				entry.Duration = parts[6]
			default:
				fmt.Println("Unknown length")
			}
			entries = append(entries, entry)
		}

		dailySummary := make(map[string]*DailySummary)
		var currentDate string

		for _, entry := range entries {
			if entry.Date != "" {
				currentDate = entry.Date
			}

			if currentDate == "" {
				continue
			}

			if _, exists := dailySummary[currentDate]; !exists {
				dailySummary[currentDate] = &DailySummary{
					Date: currentDate,
					Day:  entry.Day,
				}
			}

			summary := dailySummary[currentDate]

			switch strings.ToLower(entry.Tag) {
			case "work":
				if summary.WorkStart == "" {
					summary.WorkStart = entry.Start
				}
				summary.WorkEnd = entry.End
			case "break":
				duration, _ := parseDuration(entry.Duration)
				summary.BreakDuration += duration
			case "uni", "free", "krank", "urlaub", "feiertag":
				summary.Tag = entry.Tag
			}
		}

		var excelEntries []ExcelEntry
		for _, summary := range dailySummary {
			entry := ExcelEntry{
				Date:          summary.Date,
				Day:           summary.Day,
				WorkStart:     summary.WorkStart,
				WorkEnd:       summary.WorkEnd,
				BreakDuration: formatDuration(summary.BreakDuration),
				Tag:           summary.Tag,
			}
			excelEntries = append(excelEntries, entry)
		}

		for _, entry := range excelEntries {
			fmt.Printf("%+v\n", entry)
		}

		err = writeExcelSheet(excelEntries, name)
		if err != nil {
			return err
		}
	} else {
		fmt.Println("No Data")
	}

	return nil
}

type DailySummary struct {
	Date          string
	Day           string
	WorkStart     string
	WorkEnd       string
	BreakDuration time.Duration
	Tag           string
}

type ExcelEntry struct {
	Date          string
	Day           string
	WorkStart     string
	WorkEnd       string
	BreakDuration string
	Tag           string
}

func parseDuration(s string) (time.Duration, error) {
	parts := strings.Split(s, ":")
	if len(parts) != 3 {
		return 0, fmt.Errorf("invalid duration format: %s", s)
	}

	hours, err := strconv.Atoi(parts[0])
	if err != nil {
		return 0, err
	}

	minutes, err := strconv.Atoi(parts[1])
	if err != nil {
		return 0, err
	}

	seconds, err := strconv.Atoi(parts[2])
	if err != nil {
		return 0, err
	}
	duration, err := time.ParseDuration(fmt.Sprintf("%vh%vm%vs", hours, minutes, seconds))
	if err != nil {
		return 0, err
	}

	return duration, nil
}

func formatDuration(d time.Duration) string {
	h := d / time.Hour
	d -= h * time.Hour
	m := d / time.Minute
	d -= m * time.Minute
	s := d / time.Second
	return fmt.Sprintf("%d:%02d:%02d", h, m, s)
}

func writeExcelSheet(entries []ExcelEntry, name string) error {
	sort.Slice(entries, func(i, j int) bool {
		dateI, _ := time.Parse("2006-01-02", entries[i].Date)
		dateJ, _ := time.Parse("2006-01-02", entries[j].Date)
		return dateI.Before(dateJ)
	})

	sheetName := fmt.Sprint(time.Now().Year())
	f := excelize.NewFile()
	defer func() {
		if err := f.Close(); err != nil {
			fmt.Println(err)
		}
	}()

	index, err := f.NewSheet(sheetName)
	if err != nil {
		return err
	}

	f.SetCellValue(sheetName, "B1", "Arbeitszeiten "+sheetName)
	f.SetCellValue(sheetName, "B3", "Datum")
	f.SetCellValue(sheetName, "D3", "Arbeitszeit")
	f.SetCellValue(sheetName, "G3", "Summe")
	f.SetCellValue(sheetName, "I3", "Pause")
	f.SetCellValue(sheetName, "J3", "Summe")
	f.SetCellValue(sheetName, "K3", "Soll")
	f.SetCellValue(sheetName, "L3", "Saldo")
	f.SetCellValue(sheetName, "N3", "Saldo")
	f.SetCellValue(sheetName, "D4", "von")
	f.SetCellValue(sheetName, "E4", "bis")
	f.SetCellValue(sheetName, "G4", "brutto")
	f.SetCellValue(sheetName, "J4", "netto")
	f.SetCellValue(sheetName, "L4", "Tag")
	f.SetCellValue(sheetName, "N4", "total")

	strStyle := "hh:mm"
	timeStyle, err := f.NewStyle(&excelize.Style{
		CustomNumFmt: &strStyle,
	})
	if err != nil {
		return err
	}

	for num, entry := range entries {
		var soll string
		switch entry.Day {
		case "Mon", "Tue":
			soll = "08:00"
		case "Wed":
			soll = "04:00"
		default:
			soll = ""
		}

		row := fmt.Sprint(num + 6)

		dateValue, _ := time.Parse("2006-01-02", entry.Date)
		f.SetCellValue(sheetName, "B"+row, dateValue)

		if entry.Tag == "" {
			startTime, _ := time.Parse("15:04:05", entry.WorkStart)
			endTime, _ := time.Parse("15:04:05", entry.WorkEnd)

			startExcel := float64(startTime.Hour())/24.0 + float64(startTime.Minute())/(24.0*60.0)
			endExcel := float64(endTime.Hour())/24.0 + float64(endTime.Minute())/(24.0*60.0)

			f.SetCellValue(sheetName, "D"+row, startExcel)
			f.SetCellStyle(sheetName, "D"+row, "D"+row, timeStyle)
			f.SetCellValue(sheetName, "E"+row, endExcel)
			f.SetCellStyle(sheetName, "E"+row, "E"+row, timeStyle)

			// Formeln setzen
			f.SetCellFormula(sheetName, "G"+row, fmt.Sprintf("E%d-D%d", num+6, num+6))
			f.SetCellStyle(sheetName, "G"+row, "G"+row, timeStyle)

			f.SetCellValue(sheetName, "I"+row, entry.BreakDuration)
			f.SetCellFormula(sheetName, "J"+row, fmt.Sprintf("G%d-I%d", num+6, num+6))
			f.SetCellStyle(sheetName, "J"+row, "J"+row, timeStyle)

			f.SetCellValue(sheetName, "K"+row, soll)
			f.SetCellStyle(sheetName, "K"+row, "K"+row, timeStyle)
			f.SetCellFormula(sheetName, "L"+row, fmt.Sprintf("J%d-K%d", num+6, num+6))
			f.SetCellStyle(sheetName, "L"+row, "L"+row, timeStyle)

		} else {
			text := ""
			switch entry.Tag {
			case "uni":
				text = "Hochschule"
			case "urlaub":
				text = "Urlaub"
			case "feiertag":
				text = "Feiertag"
			default:
				text = ""
			}
			f.SetCellValue(sheetName, "D"+row, text)
		}
		f.SetCellFormula(sheetName, "N"+row, fmt.Sprintf("N%d+L%d", num+5, num+6))
		f.SetCellStyle(sheetName, "N"+row, "N"+row, timeStyle)
	}

	f.SetActiveSheet(index)
	if err := f.SaveAs(name); err != nil {
		fmt.Println(err)
	}
	return nil
}
