package handler

import (
	"Engine/internal/database"
	"Engine/internal/model"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"
)

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

func NewForecastHandler(budgets *database.BudgetRepo, actuals *database.ActualsRepo) *ForecastHandler {
	return &ForecastHandler{budgets: budgets, actuals: actuals}
}

// GET /api/v1/forecast/export?dept=ENG&year=2024&version=original
func (h *ForecastHandler) Export(w http.ResponseWriter, r *http.Request) {
	dept := r.URL.Query().Get("dept")
	yearStr := r.URL.Query().Get("year")
	version := model.BudgetVersion(r.URL.Query().Get("version"))

	if dept == "" {
		writeError(w, http.StatusBadRequest, "dept is required")
		return
	}
	year, err := strconv.Atoi(yearStr)
	if err != nil || year < 1 {
		writeError(w, http.StatusBadRequest, "year must be a valid integer")
		return
	}
	if version == "" {
		version = model.VersionOriginal
	}

	// collect actuals for all 12 periods
	var periodActuals []model.ForecastPeriodRow
	for period := 1; period <= 12; period++ {
		byGL, err := h.actuals.ListByPeriod(r.Context(), year, period, dept)
		if err != nil {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		for glCode, amount := range byGL {
			periodActuals = append(periodActuals, model.ForecastPeriodRow{
				FiscalPeriod: period,
				GLCode:       glCode,
				Amount:       amount,
			})
		}
	}

	// collect budget for all 12 periods
	var periodBudget []model.ForecastPeriodRow
	for period := 1; period <= 12; period++ {
		rows, err := h.budgets.List(r.Context(), year, period, dept, version)
		if err != nil {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		for _, b := range rows {
			periodBudget = append(periodBudget, model.ForecastPeriodRow{
				FiscalPeriod: period,
				GLCode:       b.GLCode,
				GLType:       b.GLType,
				Amount:       b.Amount,
				Currency:     b.Currency,
			})
		}
	}

	// remaining periods = periods with no actuals yet
	actualPeriods := map[int]bool{}
	for _, a := range periodActuals {
		actualPeriods[a.FiscalPeriod] = true
	}
	var remaining []int
	for p := 1; p <= 12; p++ {
		if !actualPeriods[p] {
			remaining = append(remaining, p)
		}
	}

	writeJSON(w, http.StatusOK, model.ForecastExport{
		Department:       dept,
		FiscalYear:       year,
		Version:          version,
		PeriodsActual:    periodActuals,
		PeriodsBudget:    periodBudget,
		RemainingPeriods: remaining,
	})
}

// POST /api/v1/forecast/ingest
func (h *ForecastHandler) Ingest(w http.ResponseWriter, r *http.Request) {
	var req model.ForecastIngestRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if errs := req.Valid(); len(errs) > 0 {
		writeError(w, http.StatusBadRequest, strings.Join(errs, "; "))
		return
	}

	// resolve dept ID once — all lines share the same department
	var deptID int
	if err := h.budgets.DB().QueryRowContext(r.Context(),
		`SELECT id FROM departments WHERE code = ?`, req.Department,
	).Scan(&deptID); err != nil {
		writeError(w, http.StatusBadRequest, "department not found: "+req.Department)
		return
	}

	var created []*model.Budget
	for _, line := range req.Lines {
		// resolve GL account ID per line
		var glID int
		if err := h.budgets.DB().QueryRowContext(r.Context(),
			`SELECT id FROM gl_accounts WHERE code = ?`, line.GLCode,
		).Scan(&glID); err != nil {
			writeError(w, http.StatusBadRequest, "gl_account not found: "+line.GLCode)
			return
		}

		b, err := h.budgets.Create(r.Context(), model.CreateBudgetRequest{
			FiscalYear:   req.FiscalYear,
			FiscalPeriod: line.FiscalPeriod,
			Version:      req.Version,
			DepartmentID: deptID,
			GLAccountID:  glID,
			Amount:       line.Amount,
			Currency:     line.Currency,
			CreatedBy:    req.CreatedBy,
		})
		if err != nil {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		created = append(created, b)
	}

	writeJSON(w, http.StatusCreated, created)
}
