package model

import (
	"fmt"
	"time"
)

type GLAccountType string

const (
	GLRevenue   GLAccountType = "revenue"
	GLCOGS      GLAccountType = "cogs"
	GLOpex      GLAccountType = "opex"
	GLCapex     GLAccountType = "capex"
	GLHeadcount GLAccountType = "headcount"
)

type BudgetVersion string

const (
	VersionOriginal  BudgetVersion = "original"
	VersionForecast1 BudgetVersion = "forecast_1"
	VersionForecast2 BudgetVersion = "forecast_2"
)

type Department struct {
	ID         int    `json:"id"`
	Code       string `json:"code"`
	Name       string `json:"name"`
	CostCenter string `json:"cost_center"`
	Active     bool   `json:"active"`
	CreatedAt  string `json:"created_at"`
}

type CreateDepartmentRequest struct {
	Code       string `json:"code"`
	Name       string `json:"name"`
	CostCenter string `json:"cost_center"`
	Active     bool   `json:"active"`
}

type GetDepartmentRequest struct {
	Code       *string `json:"code"`
	Name       *string `json:"name"`
	CostCenter *string `json:"cost_center"`
}

type SetDepartmentActivity struct {
	Active     bool    `json:"active"`
	Code       *string `json:"code"`
	Name       *string `json:"name"`
	CostCenter *string `json:"cost_center"`
}

type DeleteDepartmentRequest struct {
	ID int `json:"id"`
}

type DeleteGLAccountRequest struct {
	ID int `json:"id"`
}

type GLAccount struct {
	ID          int    `json:"id"`
	Code        string `json:"code"`
	Description string `json:"description"`
	Type        string `json:"type"`        // revenue | cogs | opex | capex | headcount
	FavourHigh  bool   `json:"favour_high"` // true = over-budget is good (revenue accounts)
	Active      bool   `json:"active"`
}

type GetDepartmentBudget struct {
	Code string `json:"code"`
}

type GetDepartmentActual struct {
	Code string `json:"code"`
}

type GetGLAccountBudget struct {
	Code string `json:"code"`
}

type GetGLAccountActual struct {
	Code string `json:"code"`
}

type Budget struct {
	ID           int           `json:"id"`
	FiscalYear   int           `json:"fiscal_year"`
	FiscalPeriod int           `json:"fiscal_period"`
	Version      BudgetVersion `json:"version"`
	DepartmentID int           `json:"department_id"`
	GLAccountID  int           `json:"gl_account_id"`
	Amount       float64       `json:"amount"`
	Currency     string        `json:"currency"`
	Notes        string        `json:"notes,omitempty"`
	CreatedBy    string        `json:"created_by"`
	CreatedAt    time.Time     `json:"created_at"`
	UpdatedAt    time.Time     `json:"updated_at"`
}

type CreateBudgetRequest struct {
	FiscalYear   int           `json:"fiscal_year"`
	FiscalPeriod int           `json:"fiscal_period"`
	Version      BudgetVersion `json:"version"`
	DepartmentID int           `json:"department_id"`
	GLAccountID  int           `json:"gl_account_id"`
	Amount       float64       `json:"amount"`
	Currency     string        `json:"currency"`
	Notes        string        `json:"notes"`
	CreatedBy    string        `json:"created_by"`
}

func (cBudget *CreateBudgetRequest) Valid() []string {
	var errs []string

	if cBudget.FiscalYear < 1 || cBudget.FiscalYear > 2200 {
		errs = append(errs, "fiscal_year must be between 1 and 2200")
	}
	if cBudget.FiscalPeriod < 1 || cBudget.FiscalPeriod > 12 {
		errs = append(errs, "fiscal_period must be between 1 and 12")
	}
	if cBudget.Version == "" {
		errs = append(errs, "version is required")
	}
	if cBudget.DepartmentID == 0 {
		errs = append(errs, "department_id is required")
	}
	if cBudget.GLAccountID == 0 {
		errs = append(errs, "gl_account_id is required")
	}
	if cBudget.Amount <= 0 {
		errs = append(errs, "amount must be greater than 0")
	}
	if cBudget.Currency == "" {
		errs = append(errs, "currency is required")
	}
	if cBudget.CreatedBy == "" {
		errs = append(errs, "created_by is required")
	}

	return errs
}

type UpdateBudgetRequest struct {
	ID           int    // required, not a pointer
	ChangedBy    string // required, not a pointer
	FiscalYear   *int
	FiscalPeriod *int
	Version      *string
	DepartmentID *int
	GLAccountID  *int
	Amount       *float64
	Currency     *string
	Notes        *string
}

type DeleteBudgetRequest struct {
	ID int `json:"id"`
}

func (uBudget *UpdateBudgetRequest) Valid() []string {
	var errs []string

	// Always required: identity + audit
	if uBudget.ID == 0 {
		errs = append(errs, "id is required")
	}
	if uBudget.ChangedBy == "" {
		errs = append(errs, "changed_by is required")
	}

	// Validate fields only if they were provided
	if uBudget.FiscalYear != nil {
		if *uBudget.FiscalYear < 1 || *uBudget.FiscalYear > 2200 {
			errs = append(errs, "fiscal_year must be between 1 and 2200")
		}
	}
	if uBudget.FiscalPeriod != nil {
		if *uBudget.FiscalPeriod < 1 || *uBudget.FiscalPeriod > 12 {
			errs = append(errs, "fiscal_period must be between 1 and 12")
		}
	}
	if uBudget.Amount != nil && *uBudget.Amount <= 0 {
		errs = append(errs, "amount must be greater than 0")
	}
	if uBudget.Version != nil && *uBudget.Version == "" {
		errs = append(errs, "version cannot be empty")
	}
	if uBudget.Currency != nil && *uBudget.Currency == "" {
		errs = append(errs, "currency cannot be empty")
	}

	// At least one field must be set — otherwise there's nothing to do
	if uBudget.FiscalYear == nil &&
		uBudget.FiscalPeriod == nil &&
		uBudget.Version == nil &&
		uBudget.DepartmentID == nil &&
		uBudget.GLAccountID == nil &&
		uBudget.Amount == nil &&
		uBudget.Currency == nil &&
		uBudget.Notes == nil {
		errs = append(errs, "at least one field must be provided to update")
	}

	return errs
}

type Actual struct {
	ID           int       `json:"id"`
	FiscalYear   int       `json:"fiscal_year"`
	FiscalPeriod int       `json:"fiscal_period"`
	DepartmentID int       `json:"department_id"`
	GLAccountID  int       `json:"gl_account_id"`
	GLCode       string    `json:"gl_code"`
	Amount       float64   `json:"amount"`
	Currency     string    `json:"currency"`
	Source       string    `json:"source"`
	IngestedAt   time.Time `json:"ingested_at"`
}

type IngestActualsRequest struct {
	FiscalYear   int     `json:"fiscal_year"`
	FiscalPeriod int     `json:"fiscal_period"`
	DeptCode     string  `json:"dept_code"`
	GLCode       string  `json:"gl_code"`
	Amount       float64 `json:"amount"`
	Currency     string  `json:"currency"`
	Source       string  `json:"source"`
}

func (i *IngestActualsRequest) Valid() []string {
	var errs []string

	if i.FiscalYear < 1 || i.FiscalYear > 2200 {
		errs = append(errs, "fiscal_year must be between 1 and 2200")
	}
	if i.FiscalPeriod < 1 || i.FiscalPeriod > 12 {
		errs = append(errs, "fiscal_period must be between 1 and 12")
	}
	if i.DeptCode == "" {
		errs = append(errs, "dept_code is required")
	}
	if i.GLCode == "" {
		errs = append(errs, "gl_code is required")
	}
	if i.Amount <= 0 {
		errs = append(errs, "amount must be greater than 0")
	}
	if i.Currency == "" {
		errs = append(errs, "currency is required")
	}
	if i.Source == "" {
		errs = append(errs, "source is required")
	}

	return errs
}

type VarianceStatus string

const (
	StatusFavourable   VarianceStatus = "favourable"
	StatusUnfavourable VarianceStatus = "unfavourable"
	StatusOnBudget     VarianceStatus = "on_budget"
)

type VarianceLine struct {
	GLCode        string         `json:"gl_account_id"`
	GLDescription string         `json:"gl_description"`
	GLType        GLAccountType  `json:"gl_type"`
	Budget        float64        `json:"budget"`
	Actual        float64        `json:"actual"`
	VarianceAbs   float64        `json:"variance_abs"`
	VariancePct   *float64       `json:"variance_pct"`
	Status        VarianceStatus `json:"status"`
	Currency      string         `json:"currency"`
}

type VarianceReport struct {
	Department    string         `json:"department"`
	FiscalYear    int            `json:"fiscal_year"`
	FiscalPeriod  int            `json:"fiscal_period"`
	Version       BudgetVersion  `json:"version"`
	Currency      string         `json:"currency"`
	Lines         []VarianceLine `json:"lines"`
	TotalBudget   float64        `json:"total_budget"`
	TotalActual   float64        `json:"total_actual"`
	TotalVariance float64        `json:"total_variance"`
	VariancePct   *float64       `json:"total_variance_pct"`
}

type AlertThreshold struct {
	GLCode      string         `json:"gl_code"`
	Description string         `json:"description"`
	Budget      float64        `json:"budget"`
	Actual      float64        `json:"actual"`
	VariancePct float64        `json:"variance_pct"`
	Status      VarianceStatus `json:"status"`
	Department  string         `json:"department"`
}

// BudgetRow is used internally by the variance service
type BudgetRow struct {
	GLCode        string
	GLDescription string
	GLType        GLAccountType
	FavourHigh    bool
	Amount        float64
	Currency      string
}

// GLAmountRow is a single GL line returned by the ReportRepo amount queries.
// Used as the building block for PnLSection lines.
type GLAmountRow struct {
	GLCode      string  `json:"gl_code"`
	Description string  `json:"description"`
	Amount      float64 `json:"amount"`
	Currency    string  `json:"currency"`
}

// RevenueAmounts holds the per-GL amounts for revenue accounts in a single period.
// Used as input for P&L rollup and reforecast calculations.
type RevenueAmounts struct {
	GLCode      string
	Description string
	Amount      float64
	Currency    string
}

// PnLSection is one block in the P&L (Revenue, COGS, Opex, etc.)
// Lines are the individual GL rows; Total is their sum.
type PnLSection struct {
	Lines []GLAmountRow `json:"lines"`
	Total float64       `json:"total"`
}

// PnLReport is the full P&L for a single period/dept/version.
// GrossProfit  = Revenue - COGS
// EBIT         = GrossProfit - Opex - Headcount
// NetIncome    = EBIT - Capex
type PnLReport struct {
	Department   string        `json:"department"`
	FiscalYear   int           `json:"fiscal_year"`
	FiscalPeriod int           `json:"fiscal_period"`
	Version      BudgetVersion `json:"version"`
	Currency     string        `json:"currency"`

	Revenue   PnLSection `json:"revenue"`
	COGS      PnLSection `json:"cogs"`
	Opex      PnLSection `json:"opex"`
	Headcount PnLSection `json:"headcount"`
	Capex     PnLSection `json:"capex"`

	GrossProfit float64 `json:"gross_profit"` // Revenue - COGS
	EBIT        float64 `json:"ebit"`         // GrossProfit - Opex - Headcount
	NetIncome   float64 `json:"net_income"`   // EBIT - Capex
}

type PnLRequest struct {
	FiscalYears   []int
	FiscalPeriods []int
	DeptCodes     []string
	Version       BudgetVersion
}

// ForecastIngestRequest is the payload an external model POSTs back
// after consuming the export endpoint.
type ForecastIngestRequest struct {
	Department string         `json:"department"`
	FiscalYear int            `json:"fiscal_year"`
	Version    BudgetVersion  `json:"version"`
	CreatedBy  string         `json:"created_by"`
	Lines      []ForecastLine `json:"lines"`
}

type ForecastLine struct {
	FiscalPeriod int     `json:"period"`
	GLCode       string  `json:"gl_code"`
	Amount       float64 `json:"amount"`
	Currency     string  `json:"currency"`
}

func (f *ForecastIngestRequest) Valid() []string {
	var errs []string

	if f.Department == "" {
		errs = append(errs, "department is required")
	}
	if f.FiscalYear < 1 || f.FiscalYear > 2200 {
		errs = append(errs, "fiscal_year must be between 1 and 2200")
	}
	if f.Version == "" {
		errs = append(errs, "version is required")
	}
	if f.Version == VersionOriginal {
		errs = append(errs, "version cannot be original — forecasts must use forecast_1, forecast_2, or forecast_3")
	}
	if f.CreatedBy == "" {
		errs = append(errs, "created_by is required")
	}
	if len(f.Lines) == 0 {
		errs = append(errs, "lines must contain at least one record")
	}

	for i, line := range f.Lines {
		if line.FiscalPeriod < 1 || line.FiscalPeriod > 12 {
			errs = append(errs, fmt.Sprintf("lines[%d]: period must be between 1 and 12", i))
		}
		if line.GLCode == "" {
			errs = append(errs, fmt.Sprintf("lines[%d]: gl_code is required", i))
		}
		if line.Amount <= 0 {
			errs = append(errs, fmt.Sprintf("lines[%d]: amount must be greater than 0", i))
		}
		if line.Currency == "" {
			errs = append(errs, fmt.Sprintf("lines[%d]: currency is required", i))
		}
	}

	return errs
}

type ForecastPeriodRow struct {
	FiscalPeriod int           `json:"period"`
	GLCode       string        `json:"gl_code"`
	GLType       GLAccountType `json:"gl_type,omitempty"`
	Amount       float64       `json:"amount"`
	Currency     string        `json:"currency,omitempty"`
}

type ForecastExport struct {
	Department       string              `json:"department"`
	FiscalYear       int                 `json:"fiscal_year"`
	Version          BudgetVersion       `json:"version"`
	PeriodsActual    []ForecastPeriodRow `json:"periods_actual"`
	PeriodsBudget    []ForecastPeriodRow `json:"periods_budget"`
	RemainingPeriods []int               `json:"remaining_periods"`
}
