package templates

import (
	"bytes"
	"fmt"
	"strings"

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

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

type UsfdReportExcel struct{}

func (u *UsfdReportExcel) CreateUsfdReportExcel(reports []DivisionTripReportModel, divisionName string, reportName string, reportDate string) ([]byte, error) {
	f := excelize.NewFile()
	defer func() {
		_ = f.Close()
	}()

	sheet := reportName

	if err := f.SetSheetName("Sheet1", sheet); err != nil {
		return nil, err
	}

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

	// Styles
	titleStyle, err := u.titleStyle(f)
	if err != nil {
		return nil, err
	}

	dateStyle, err := u.dateStyle(f)
	if err != nil {
		return nil, err
	}

	metaLabelStyle, err := u.metaLabelStyle(f)
	if err != nil {
		return nil, err
	}

	metaValueStyle, err := u.metaValueStyle(f)
	if err != nil {
		return nil, err
	}

	headerStyle, err := u.headerStyle(f)
	if err != nil {
		return nil, err
	}

	oddStyles, err := u.dataRowStyles(f, false)
	if err != nil {
		return nil, err
	}

	evenStyles, err := u.dataRowStyles(f, true)
	if err != nil {
		return nil, err
	}

	// Row 1: Title and report date
	if err := f.SetCellValue(sheet, "A1", strings.ToUpper(reportName)); err != nil {
		return nil, err
	}
	if err := f.SetCellStyle(sheet, "A1", "A1", titleStyle); err != nil {
		return nil, err
	}
	if err := f.MergeCell(sheet, "A1", "E1"); err != nil {
		return nil, err
	}

	if err := f.SetCellValue(sheet, "F1", reportDate); err != nil {
		return nil, err
	}
	if err := f.SetCellStyle(sheet, "F1", "F1", dateStyle); err != nil {
		return nil, err
	}
	if err := f.MergeCell(sheet, "F1", "G1"); err != nil {
		return nil, err
	}

	if err := f.SetRowHeight(sheet, 1, 22); err != nil {
		return nil, err
	}

	// Row 2: Report metadata
	metadata := []struct {
		labelCell string
		valueCell string
		label     string
		value     interface{}
	}{
		{"A2", "B2", "Division: ", divisionName + "    "},
		{"C2", "D2", "Date: ", reportDate + "    "},
		{"E2", "F2", "Total Devices: ", len(reports)},
	}

	for _, item := range metadata {
		if err := f.SetCellValue(sheet, item.labelCell, item.label); err != nil {
			return nil, err
		}
		if err := f.SetCellValue(sheet, item.valueCell, item.value); err != nil {
			return nil, err
		}

		if err := f.SetCellStyle(
			sheet, item.labelCell, item.labelCell, metaLabelStyle,
		); err != nil {
			return nil, err
		}
		if err := f.SetCellStyle(
			sheet, item.valueCell, item.valueCell, metaValueStyle,
		); err != nil {
			return nil, err
		}
	}

	if err := f.SetRowHeight(sheet, 2, 16); err != nil {
		return nil, err
	}

	// Row 3: Spacer
	if err := f.SetRowHeight(sheet, 3, 8); err != nil {
		return nil, err
	}

	// Row 4: Column headers
	headers := []string{
		"Sr.No",
		"Name",
		"OnTrack Time",
		"Actual KM",
		"Walking Distance",
		"Max Speed",
		"Start% - End% Battery",
	}

	for i, header := range headers {
		cell, err := excelize.CoordinatesToCellName(i+1, 4)
		if err != nil {
			return nil, err
		}

		if err := f.SetCellValue(sheet, cell, header); err != nil {
			return nil, err
		}
		if err := f.SetCellStyle(sheet, cell, cell, headerStyle); err != nil {
			return nil, err
		}
	}

	if err := f.SetRowHeight(sheet, 4, 18); err != nil {
		return nil, err
	}

	// Rows 5 onward: Report data
	for idx, report := range reports {
		row := idx + 5

		styles := oddStyles
		if idx%2 != 0 {
			styles = evenStyles
		}

		battery := u.batteryRange(report)

		values := []any{
			idx + 1,
			DefaultValue(*report.DeviceName),
			DefaultTime(report.ActualStartTime) +
				" - " +
				DefaultTime(report.ActualEndTime),
			fmt.Sprintf("%.2f", DerefFloat64(report.DistanceCoverTrip)),
			u.walkingDistance(report.WalkingDistance),
			report.TripMaxSpeed,
			battery,
		}

		for col, value := range values {
			cell, err := excelize.CoordinatesToCellName(col+1, row)
			if err != nil {
				return nil, err
			}

			if err := f.SetCellValue(sheet, cell, value); err != nil {
				return nil, err
			}

			style := styles[1]
			if col == 1 {
				style = styles[0]
			}

			if err := f.SetCellStyle(sheet, cell, cell, style); err != nil {
				return nil, err
			}
		}

		if err := f.SetRowHeight(sheet, row, 16); err != nil {
			return nil, err
		}
	}

	// Return XLSX as bytes.
	var output bytes.Buffer
	if err := f.Write(&output); err != nil {
		return nil, err
	}

	return output.Bytes(), nil
}

// -----------------------------------------------------------------------------
// Styles
// -----------------------------------------------------------------------------

func (u *UsfdReportExcel) titleStyle(f *excelize.File) (int, error) {
	return f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   13,
			Bold:   true,
			Color:  "1A1A1A",
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
		},
	})
}

func (u *UsfdReportExcel) dateStyle(f *excelize.File) (int, error) {
	return f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   13,
			Bold:   true,
			Color:  "5C70C4",
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
		},
	})
}

func (u *UsfdReportExcel) metaLabelStyle(f *excelize.File) (int, error) {
	return f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   8,
			Color:  "555555",
		},
	})
}

func (u *UsfdReportExcel) metaValueStyle(f *excelize.File) (int, error) {
	return f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   8,
			Bold:   true,
			Color:  "1A1A1A",
		},
	})
}

func (u *UsfdReportExcel) headerStyle(f *excelize.File) (int, error) {
	return f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   8,
			Bold:   true,
			Color:  "FFFFFF",
		},
		Fill: excelize.Fill{
			Type:    "pattern",
			Pattern: 1,
			Color:   []string{"015958"},
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
			WrapText:   true,
		},
		Border: []excelize.Border{
			{Type: "top", Color: "014545", Style: 1},
			{Type: "bottom", Color: "014545", Style: 1},
			{Type: "left", Color: "014545", Style: 1},
			{Type: "right", Color: "014545", Style: 1},
		},
	})
}

// [0] = name cell (bold teal), [1] = normal cell.
func (u *UsfdReportExcel) dataRowStyles(
	f *excelize.File,
	even bool,
) ([]int, error) {
	background := "FFFFFF"
	if even {
		background = "F4FAFA"
	}

	borders := []excelize.Border{
		{Type: "top", Color: "DD DDEE", Style: 1},
		{Type: "bottom", Color: "DD DDEE", Style: 1},
		{Type: "left", Color: "DD DDEE", Style: 1},
		{Type: "right", Color: "DD DDEE", Style: 1},
	}

	// Name style: bold teal, left aligned.
	nameStyle, err := f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   8,
			Bold:   true,
			Color:  "015958",
		},
		Fill: excelize.Fill{
			Type:    "pattern",
			Pattern: 1,
			Color:   []string{background},
		},
		Alignment: &excelize.Alignment{
			Horizontal: "left",
			Vertical:   "center",
		},
		Border: borders,
	})
	if err != nil {
		return nil, err
	}

	// Normal style: centered.
	normalStyle, err := f.NewStyle(&excelize.Style{
		Font: &excelize.Font{
			Family: "Arial",
			Size:   8,
			Color:  "222222",
		},
		Fill: excelize.Fill{
			Type:    "pattern",
			Pattern: 1,
			Color:   []string{background},
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center",
			Vertical:   "center",
		},
		Border: borders,
	})
	if err != nil {
		return nil, err
	}

	return []int{nameStyle, normalStyle}, nil
}

// -----------------------------------------------------------------------------
// Value helpers
// -----------------------------------------------------------------------------

func (u *UsfdReportExcel) walkingDistance(value *float64) string {
	if value == nil {
		return "--"
	}
	return fmt.Sprintf("%.2f", *value)
}

// The Java implementation reads the first and last trip's START battery
// status. This intentionally preserves that behavior.
func (u *UsfdReportExcel) batteryRange(report DivisionTripReportModel) string {
	if len(report.TripList) == 0 {
		return "- - -"
	}

	start := BatteryPercentFunc(
		derefInt32(report.TripList[0].DeviceTripStartBatteryStatus),
	)
	last := BatteryPercentFunc(
		derefInt32(report.TripList[len(report.TripList)-1].DeviceTripStartBatteryStatus),
	)

	return start + "% - " + last + "%"
}

func derefInt32(value *int32) int32 {
	if value == nil {
		return -1
	}
	return *value
}

func BatteryPercentFunc(level int32) string {
	if level < 0 {
		return "0"
	}

	percent := (float64(level) / 6) * 100
	return fmt.Sprintf("%.2f", percent)
}
