package models

import (
	"time"

	"odoo-go/pkg/orm"
)

// initAccountBudget registers budget planning models.
// Mirrors: odoo/addons/account_budget/models/account_budget.py
//
// crossovered.budget defines a budget with a date range and responsibility.
// crossovered.budget.lines defines individual budget lines per analytic account.
func initAccountBudget() {
	// -- Budget Header --
	m := orm.NewModel("crossovered.budget", orm.ModelOpts{
		Description: "Budget",
		Order:       "date_from desc",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Budget Name", Required: true}),
		orm.Many2one("user_id", "res.users", orm.FieldOpts{String: "Responsible"}),
		orm.Date("date_from", orm.FieldOpts{String: "Start Date", Required: true}),
		orm.Date("date_to", orm.FieldOpts{String: "End Date", Required: true}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "Draft"},
			{Value: "cancel", Label: "Cancelled"},
			{Value: "confirm", Label: "Confirmed"},
			{Value: "validate", Label: "Validated"},
			{Value: "done", Label: "Done"},
		}, orm.FieldOpts{String: "Status", Default: "draft", Required: true}),
		orm.One2many("crossovered_budget_line", "crossovered.budget.lines", "crossovered_budget_id", orm.FieldOpts{
			String: "Budget Lines",
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company", Required: true}),
	)

	// action_budget_confirm: draft -> confirm
	m.RegisterMethod("action_budget_confirm", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			env.Tx().Exec(env.Ctx(),
				`UPDATE crossovered_budget SET state = 'confirm' WHERE id = $1 AND state = 'draft'`, id)
		}
		return true, nil
	})

	// action_budget_validate: confirm -> validate
	m.RegisterMethod("action_budget_validate", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			env.Tx().Exec(env.Ctx(),
				`UPDATE crossovered_budget SET state = 'validate' WHERE id = $1 AND state = 'confirm'`, id)
		}
		return true, nil
	})

	// action_budget_done: validate -> done
	m.RegisterMethod("action_budget_done", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			env.Tx().Exec(env.Ctx(),
				`UPDATE crossovered_budget SET state = 'done' WHERE id = $1 AND state = 'validate'`, id)
		}
		return true, nil
	})

	// action_budget_cancel: any -> cancel
	m.RegisterMethod("action_budget_cancel", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			env.Tx().Exec(env.Ctx(),
				`UPDATE crossovered_budget SET state = 'cancel' WHERE id = $1`, id)
		}
		return true, nil
	})

	// action_budget_draft: cancel -> draft
	m.RegisterMethod("action_budget_draft", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			env.Tx().Exec(env.Ctx(),
				`UPDATE crossovered_budget SET state = 'draft' WHERE id = $1 AND state = 'cancel'`, id)
		}
		return true, nil
	})

	m.DefaultGet = func(env *orm.Environment, fields []string) orm.Values {
		vals := orm.Values{
			"state":     "draft",
			"date_from": time.Now().Format("2006-01-02"),
			"date_to":   time.Now().AddDate(1, 0, 0).Format("2006-01-02"),
		}
		companyID := env.CompanyID()
		if companyID > 0 {
			vals["company_id"] = companyID
		}
		return vals
	}

	// -- Budget Lines --
	bl := orm.NewModel("crossovered.budget.lines", orm.ModelOpts{
		Description: "Budget Line",
		Order:       "date_from",
	})

	bl.AddFields(
		orm.Many2one("crossovered_budget_id", "crossovered.budget", orm.FieldOpts{
			String: "Budget", Required: true, OnDelete: orm.OnDeleteCascade,
		}),
		orm.Many2one("analytic_account_id", "account.analytic.account", orm.FieldOpts{
			String: "Analytic Account",
		}),
		orm.Many2one("general_budget_id", "account.budget.post", orm.FieldOpts{
			String: "Budgetary Position",
		}),
		orm.Date("date_from", orm.FieldOpts{String: "Start Date", Required: true}),
		orm.Date("date_to", orm.FieldOpts{String: "End Date", Required: true}),
		orm.Monetary("planned_amount", orm.FieldOpts{
			String: "Planned Amount", Required: true, CurrencyField: "currency_id",
		}),
		orm.Monetary("practical_amount", orm.FieldOpts{
			String: "Practical Amount", Compute: "_compute_practical_amount", CurrencyField: "currency_id",
		}),
		orm.Float("percentage", orm.FieldOpts{
			String: "Achievement", Compute: "_compute_percentage",
		}),
		orm.Many2one("currency_id", "res.currency", orm.FieldOpts{String: "Currency"}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Related: "crossovered_budget_id.company_id",
		}),
		orm.Many2one("paid_date", "res.company", orm.FieldOpts{String: "Paid Date"}),
		orm.Boolean("is_above_budget", orm.FieldOpts{
			String: "Above Budget", Compute: "_compute_is_above_budget",
		}),
	)

	// _compute_practical_amount: sum of posted journal entries for the analytic account
	// in the budget line's date range.
	// Mirrors: odoo/addons/account_budget/models/account_budget.py _compute_practical_amount()
	bl.RegisterCompute("practical_amount", func(rs *orm.Recordset) (orm.Values, error) {
		env := rs.Env()
		lineID := rs.IDs()[0]

		var analyticID *int64
		var dateFrom, dateTo string
		env.Tx().QueryRow(env.Ctx(),
			`SELECT analytic_account_id, date_from::text, date_to::text
			 FROM crossovered_budget_lines WHERE id = $1`, lineID,
		).Scan(&analyticID, &dateFrom, &dateTo)

		if analyticID == nil || *analyticID == 0 {
			return orm.Values{"practical_amount": 0.0}, nil
		}

		var total float64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(SUM(al.amount::float8), 0)
			 FROM account_analytic_line al
			 WHERE al.account_id = $1 AND al.date >= $2 AND al.date <= $3`,
			*analyticID, dateFrom, dateTo,
		).Scan(&total)

		return orm.Values{"practical_amount": total}, nil
	})

	// _compute_percentage: practical / planned * 100
	bl.RegisterCompute("percentage", func(rs *orm.Recordset) (orm.Values, error) {
		env := rs.Env()
		lineID := rs.IDs()[0]

		var planned, practical float64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(planned_amount::float8, 0), COALESCE(practical_amount::float8, 0)
			 FROM crossovered_budget_lines WHERE id = $1`, lineID,
		).Scan(&planned, &practical)

		pct := 0.0
		if planned != 0 {
			pct = (practical / planned) * 100
		}

		return orm.Values{"percentage": pct}, nil
	})

	// _compute_is_above_budget
	bl.RegisterCompute("is_above_budget", func(rs *orm.Recordset) (orm.Values, error) {
		env := rs.Env()
		lineID := rs.IDs()[0]

		var planned, practical float64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(planned_amount::float8, 0), COALESCE(practical_amount::float8, 0)
			 FROM crossovered_budget_lines WHERE id = $1`, lineID,
		).Scan(&planned, &practical)

		above := false
		if planned > 0 {
			above = practical > planned
		} else if planned < 0 {
			above = practical < planned
		}

		return orm.Values{"is_above_budget": above}, nil
	})

	// -- Budgetary Position --
	// account.budget.post groups accounts for budgeting purposes.
	// Mirrors: odoo/addons/account_budget/models/account_budget.py AccountBudgetPost
	bp := orm.NewModel("account.budget.post", orm.ModelOpts{
		Description: "Budgetary Position",
		Order:       "name",
	})

	bp.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Name", Required: true}),
		orm.Many2many("account_ids", "account.account", orm.FieldOpts{
			String:   "Accounts",
			Relation: "account_budget_post_account_rel",
			Column1:  "budget_post_id",
			Column2:  "account_id",
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company", Required: true}),
	)
}
