package service

import (
	"context"
	"math"

	"Engine/internal/database"
	"Engine/internal/model"
)

type VarianceService struct {
	budgets *database.BudgetRepo
	actuals *database.ActualsRepo
}

func NewVarianceService(b *database.BudgetRepo, a *database.ActualsRepo) *VarianceService {
	return &VarianceService{budgets: b, actuals: a}
}

type VarianceFilter struct {
	FiscalYear   int
	FiscalPeriod int
	DeptCode     string
	Version      model.BudgetVersion
}

func (s *VarianceService) Report(ctx context.Context, f VarianceFilter) (*model.VarianceReport, error) {
	budgets, err := s.budgets.List(ctx, f.FiscalYear, f.FiscalPeriod, f.DeptCode, f.Version)
	if err != nil {
		return nil, err
	}

	actualsByGL, err := s.actuals.ListByPeriod(ctx, f.FiscalYear, f.FiscalPeriod, f.DeptCode)
	if err != nil {
		return nil, err
	}

	report := &model.VarianceReport{
		FiscalYear:   f.FiscalYear,
		FiscalPeriod: f.FiscalPeriod,
		Version:      f.Version,
		Department:   f.DeptCode,
		Currency:     "DKK",
	}

	for _, b := range budgets {
		actual := actualsByGL[b.GLCode]
		varAbs := actual - b.Amount

		var varPct *float64
		if b.Amount != 0 {
			v := math.Round((varAbs/b.Amount)*10000) / 100
			varPct = &v
		}

		report.Lines = append(report.Lines, model.VarianceLine{
			GLCode:        b.GLCode,
			GLDescription: b.GLDescription,
			GLType:        b.GLType,
			Budget:        b.Amount,
			Actual:        actual,
			VarianceAbs:   varAbs,
			VariancePct:   varPct,
			Status:        computeStatus(varAbs, b.FavourHigh),
			Currency:      b.Currency,
		})

		report.TotalBudget += b.Amount
		report.TotalActual += actual
	}

	report.TotalVariance = report.TotalActual - report.TotalBudget
	if report.TotalBudget != 0 {
		v := math.Round((report.TotalVariance/report.TotalBudget)*10000) / 100
		report.VariancePct = &v
	}

	return report, nil
}

func (s *VarianceService) Alerts(ctx context.Context, f VarianceFilter, thresholdPct float64) ([]model.AlertThreshold, error) {
	report, err := s.Report(ctx, f)
	if err != nil {
		return nil, err
	}

	var alerts []model.AlertThreshold
	for _, line := range report.Lines {
		if line.VariancePct == nil {
			continue
		}
		if math.Abs(*line.VariancePct) >= thresholdPct {
			alerts = append(alerts, model.AlertThreshold{
				GLCode:      line.GLCode,
				Description: line.GLDescription,
				Budget:      line.Budget,
				Actual:      line.Actual,
				VariancePct: *line.VariancePct,
				Status:      line.Status,
				Department:  report.Department,
			})
		}
	}
	return alerts, nil
}

func computeStatus(varAbs float64, favourHigh bool) model.VarianceStatus {
	const epsilon = 0.01
	if math.Abs(varAbs) < epsilon {
		return model.StatusOnBudget
	}
	if (favourHigh && varAbs > 0) || (!favourHigh && varAbs < 0) {
		return model.StatusFavourable
	}
	return model.StatusUnfavourable
}
