package templates

import (
	"bytes"
	"fmt"
	"strings"

	. "primesys_email/src/models/report"
	"primesys_email/src/templates/utils"
	. "primesys_email/src/templates/utils"

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

type PatrolmanReportExcel struct{}

func (p *PatrolmanReportExcel) CreatePatrolmanReportExcel(reports []DivisionTripReportModel, divisionName string, reportDate string) ([]byte, error) {
	f := excelize.NewFile()
	defer f.Close()

	const sheet = "Patrol Man Report"

	index, err := f.NewSheet(sheet)
	if err != nil {
		return nil, err
	}
	f.DeleteSheet("Sheet1")
	f.SetActiveSheet(index)

	widths := []float64{32, 22, 22, 20, 22, 16, 16, 14, 14, 20, 20}
	for i, width := range widths {
		col, _ := excelize.ColumnNumberToName(i + 1)
		if err := f.SetColWidth(sheet, col, col, width); err != nil {
			return nil, err
		}
	}

	titleStyle := TitleStyle(f)
	metaLabelStyle := MetaLabelStyle(f)
	metaValueStyle := MetaValueStyle(f)
	headerStyle := HeaderStyle(f)
	oddStyle := DataStyle(f, false)
	evenStyle := DataStyle(f, true)
	remarkStyles := RemarkStyles(f)

	// Title
	f.SetCellValue(sheet, "A1", "Division Patrol Man Report")
	f.MergeCell(sheet, "A1", "K1")
	f.SetCellStyle(sheet, "A1", "K1", titleStyle)
	f.SetRowHeight(sheet, 1, 20)

	// Metadata
	p.setMetaPair(f, sheet, "A2", "B2", "Division: ", divisionName, metaLabelStyle, metaValueStyle)
	p.setMetaPair(f, sheet, "C2", "D2", "Date: ", reportDate, metaLabelStyle, metaValueStyle)
	p.setMetaPair(f, sheet, "E2", "F2", "Total Devices: ", fmt.Sprint(len(reports)), metaLabelStyle, metaValueStyle)

	headers := []string{
		"Name", "Exp. Location", "Act. Location", "Exp. Time",
		"OnTrack Time", "Exp. KM", "Act. KM", "Exp. Trip",
		"Act. Trip", "Battery S–E", "Remarks",
	}

	headerRow := 4
	for i, header := range headers {
		cell, _ := excelize.CoordinatesToCellName(i+1, headerRow)
		f.SetCellValue(sheet, cell, header)
		f.SetCellStyle(sheet, cell, cell, headerStyle)
	}
	f.SetRowHeight(sheet, headerRow, 18)

	for i, r := range reports {
		row := i + 5
		style := oddStyle
		if i%2 != 0 {
			style = evenStyle
		}

		name := stringValue(r.DeviceName)
		remark := stringValue(r.Remark)
		if remark == "" {
			remark = "-"
		}

		values := []any{
			name,
			fmt.Sprintf("%s–%s", DefaultFloat(r.TripStartKm), DefaultFloat(r.TripEndKm)),
			fmt.Sprintf("%s–%s", DefaultFloat(r.TripActualStartKm), DefaultFloat(r.TripActualEndKm)),
			fmt.Sprintf("%s–%s", DefaultTime(r.TripStartTime), DefaultTime(r.TripEndTime)),
			fmt.Sprintf("%s–%s", DefaultTime(r.ActualStartTime), DefaultTime(r.ActualEndTime)),
			DefaultFloat(r.TripDistanceTobeCoverKm),
			DefaultFloat(r.DistanceCoverTrip),
			DefaultInt(r.AllocatedTrips),
			DefaultInt(r.ActualTrips),
			p.batteryRange(r.TripList),
			remark,
		}

		for col, value := range values {
			cell, _ := excelize.CoordinatesToCellName(col+1, row)
			f.SetCellValue(sheet, cell, value)

			cellStyle := style
			if col == 0 {
				cellStyle = NameStyle(f)
			}
			if col == 10 {
				cellStyle = RemarkStyleFor(f, remark, remarkStyles)
			}

			f.SetCellStyle(sheet, cell, cell, cellStyle)
		}

		f.SetRowHeight(sheet, headerRow, 18)
	}

	var out bytes.Buffer
	if err := f.Write(&out); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

// Battery range for the first trip.
func (p *PatrolmanReportExcel) batteryRange(trips []TripReportStatusModel) string {
	var startBattery, endBattery int32

	if len(trips) > 0 {
		if trips[0].DeviceTripStartBatteryStatus != nil {
			startBattery = *trips[0].DeviceTripStartBatteryStatus
		}

		if trips[0].DeviceTripEndBatteryStatus != nil {
			endBattery = *trips[0].DeviceTripEndBatteryStatus
		}
	}

	return fmt.Sprintf("%s%% – %s%%", utils.BatteryPercent(startBattery), utils.BatteryPercent(endBattery))
}

func (p *PatrolmanReportExcel) setMetaPair(
	f *excelize.File,
	sheet, labelCell, valueCell, label, value string,
	labelStyle, valueStyle int,
) {
	f.SetCellValue(sheet, labelCell, label)
	f.SetCellValue(sheet, valueCell, value)
	f.SetCellStyle(sheet, labelCell, labelCell, labelStyle)
	f.SetCellStyle(sheet, valueCell, valueCell, valueStyle)
}

func stringValue(v *string) string {
	if v == nil || strings.TrimSpace(*v) == "" {
		return "-"
	}
	return *v
}
