package models

import (
	"fmt"
	"time"

	"odoo-go/pkg/orm"
)

// initPurchaseOrder registers purchase.order and purchase.order.line.
// Mirrors: odoo/addons/purchase/models/purchase_order.py

func initPurchaseOrder() {
	// purchase.order — the purchase order header
	m := orm.NewModel("purchase.order", orm.ModelOpts{
		Description: "Purchase Order",
		Order:       "priority desc, id desc",
		RecName:     "name",
	})

	// -- Identity --
	m.AddFields(
		orm.Char("name", orm.FieldOpts{
			String: "Order Reference", Required: true, Index: true, Readonly: true, Default: "New",
		}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "RFQ"},
			{Value: "sent", Label: "RFQ Sent"},
			{Value: "to approve", Label: "To Approve"},
			{Value: "purchase", Label: "Purchase Order"},
			{Value: "done", Label: "Locked"},
			{Value: "cancel", Label: "Cancelled"},
		}, orm.FieldOpts{String: "Status", Default: "draft", Readonly: true, Index: true}),
		orm.Selection("priority", []orm.SelectionItem{
			{Value: "0", Label: "Normal"},
			{Value: "1", Label: "Urgent"},
		}, orm.FieldOpts{String: "Priority", Default: "0", Index: true}),
	)

	// -- Partner & Dates --
	m.AddFields(
		orm.Many2one("partner_id", "res.partner", orm.FieldOpts{
			String: "Vendor", Required: true, Index: true,
		}),
		orm.Datetime("date_order", orm.FieldOpts{
			String: "Order Deadline", Required: true, Index: true, Default: "today",
		}),
		orm.Datetime("date_planned", orm.FieldOpts{
			String: "Expected Arrival",
		}),
		orm.Datetime("date_approve", orm.FieldOpts{
			String: "Confirmation Date", Readonly: true, Index: true,
		}),
	)

	// -- Company & Currency --
	m.AddFields(
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Many2one("currency_id", "res.currency", orm.FieldOpts{
			String: "Currency", Required: true,
		}),
	)

	// -- Lines --
	m.AddFields(
		orm.One2many("order_line", "purchase.order.line", "order_id", orm.FieldOpts{
			String: "Order Lines",
		}),
	)

	// -- Amounts --
	m.AddFields(
		orm.Monetary("amount_untaxed", orm.FieldOpts{
			String: "Untaxed Amount", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
		}),
		orm.Monetary("amount_tax", orm.FieldOpts{
			String: "Taxes", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
		}),
		orm.Monetary("amount_total", orm.FieldOpts{
			String: "Total", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
		}),
	)

	// -- Invoice Status --
	m.AddFields(
		orm.Selection("invoice_status", []orm.SelectionItem{
			{Value: "no", Label: "Nothing to Bill"},
			{Value: "to invoice", Label: "Waiting Bills"},
			{Value: "invoiced", Label: "Fully Billed"},
		}, orm.FieldOpts{String: "Billing Status", Compute: "_compute_invoice_status", Store: true}),
	)

	// -- Accounting --
	m.AddFields(
		orm.Many2one("fiscal_position_id", "account.fiscal.position", orm.FieldOpts{
			String: "Fiscal Position",
		}),
		orm.Many2one("payment_term_id", "account.payment.term", orm.FieldOpts{
			String: "Payment Terms",
		}),
	)

	// -- Notes --
	m.AddFields(
		orm.Text("notes", orm.FieldOpts{String: "Terms and Conditions"}),
		orm.Char("origin", orm.FieldOpts{String: "Source Document"}),
	)

	// -- DefaultGet: Provide dynamic defaults for new records --
	// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.default_get()
	// Supplies company_id, currency_id, date_order when creating a new PO.
	m.DefaultGet = func(env *orm.Environment, fields []string) orm.Values {
		vals := make(orm.Values)

		// Default company from the current user's session
		companyID := env.CompanyID()
		if companyID > 0 {
			vals["company_id"] = companyID
		}

		// Default currency from the company
		var currencyID int64
		err := env.Tx().QueryRow(env.Ctx(),
			`SELECT currency_id FROM res_company WHERE id = $1`, companyID).Scan(&currencyID)
		if err == nil && currencyID > 0 {
			vals["currency_id"] = currencyID
		}

		// Default date_order = now
		vals["date_order"] = time.Now().Format("2006-01-02 15:04:05")

		return vals
	}

	// button_confirm: draft → purchase
	m.RegisterMethod("button_confirm", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			var state string
			env.Tx().QueryRow(env.Ctx(),
				`SELECT state FROM purchase_order WHERE id = $1`, id).Scan(&state)
			if state != "draft" && state != "sent" {
				return nil, fmt.Errorf("purchase: can only confirm draft orders")
			}
			env.Tx().Exec(env.Ctx(),
				`UPDATE purchase_order SET state = 'purchase', date_approve = NOW() WHERE id = $1`, id)
		}
		return true, nil
	})

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

	// button_draft: Reset a cancelled PO back to draft (RFQ).
	// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.button_draft()
	m.RegisterMethod("button_draft", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, poID := range rs.IDs() {
			env.Tx().Exec(env.Ctx(), `UPDATE purchase_order SET state = 'draft' WHERE id = $1 AND state = 'cancel'`, poID)
		}
		return true, nil
	})

	// action_create_bill: Generate a vendor bill (account.move in_invoice) from a confirmed PO.
	// Mirrors: odoo/addons/purchase/models/purchase_order.py PurchaseOrder.action_create_invoice()
	m.RegisterMethod("action_create_bill", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		var billIDs []int64

		for _, poID := range rs.IDs() {
			var partnerID, companyID, currencyID int64
			err := env.Tx().QueryRow(env.Ctx(),
				`SELECT partner_id, company_id, currency_id FROM purchase_order WHERE id = $1`,
				poID).Scan(&partnerID, &companyID, &currencyID)
			if err != nil {
				return nil, fmt.Errorf("purchase: read PO %d for bill: %w", poID, err)
			}

			// Find purchase journal
			var journalID int64
			env.Tx().QueryRow(env.Ctx(),
				`SELECT id FROM account_journal WHERE type = 'purchase' AND company_id = $1 LIMIT 1`,
				companyID).Scan(&journalID)
			if journalID == 0 {
				// Fallback: first available journal
				env.Tx().QueryRow(env.Ctx(),
					`SELECT id FROM account_journal WHERE company_id = $1 ORDER BY id LIMIT 1`,
					companyID).Scan(&journalID)
			}

			// Read PO lines to generate invoice lines
			rows, err := env.Tx().Query(env.Ctx(),
				`SELECT id, COALESCE(name,''), COALESCE(product_qty,1), COALESCE(price_unit,0), COALESCE(discount,0)
				 FROM purchase_order_line
				 WHERE order_id = $1 ORDER BY sequence, id`, poID)
			if err != nil {
				return nil, fmt.Errorf("purchase: read PO lines %d: %w", poID, err)
			}

			type poLine struct {
				id       int64
				name     string
				qty      float64
				price    float64
				discount float64
			}
			var lines []poLine
			for rows.Next() {
				var l poLine
				if err := rows.Scan(&l.id, &l.name, &l.qty, &l.price, &l.discount); err != nil {
					rows.Close()
					return nil, err
				}
				lines = append(lines, l)
			}
			rows.Close()

			// Create the vendor bill
			var billID int64
			err = env.Tx().QueryRow(env.Ctx(),
				`INSERT INTO account_move
				 (name, move_type, state, date, partner_id, journal_id, company_id, currency_id, invoice_origin)
				 VALUES ('/', 'in_invoice', 'draft', NOW(), $1, $2, $3, $4, $5) RETURNING id`,
				partnerID, journalID, companyID, currencyID,
				fmt.Sprintf("PO%d", poID)).Scan(&billID)
			if err != nil {
				return nil, fmt.Errorf("purchase: create bill for PO %d: %w", poID, err)
			}

			// Try to generate a proper sequence name
			seq, seqErr := orm.NextByCode(env, "account.move.in_invoice")
			if seqErr != nil {
				seq, seqErr = orm.NextByCode(env, "account.move")
			}
			if seqErr == nil && seq != "" {
				env.Tx().Exec(env.Ctx(),
					`UPDATE account_move SET name = $1 WHERE id = $2`, seq, billID)
			}

			// Create invoice lines for each PO line
			for _, l := range lines {
				subtotal := l.qty * l.price * (1 - l.discount/100)
				env.Tx().Exec(env.Ctx(),
					`INSERT INTO account_move_line
					 (move_id, name, quantity, price_unit, discount, debit, credit, balance,
					  display_type, company_id, journal_id, account_id)
					 VALUES ($1, $2, $3, $4, $5, $6, 0, $6, 'product', $7, $8,
					  COALESCE((SELECT id FROM account_account WHERE company_id = $7 LIMIT 1), 1))`,
					billID, l.name, l.qty, l.price, l.discount, subtotal,
					companyID, journalID)
			}

			// Update qty_invoiced on PO lines
			for _, l := range lines {
				env.Tx().Exec(env.Ctx(),
					`UPDATE purchase_order_line SET qty_invoiced = COALESCE(qty_invoiced, 0) + $1 WHERE id = $2`,
					l.qty, l.id)
			}

			billIDs = append(billIDs, billID)

			// Update PO invoice_status
			_, err = env.Tx().Exec(env.Ctx(),
				`UPDATE purchase_order SET invoice_status = 'invoiced' WHERE id = $1`, poID)
			if err != nil {
				return nil, fmt.Errorf("purchase: update invoice status for PO %d: %w", poID, err)
			}
		}
		return billIDs, nil
	})

	// BeforeCreate: auto-assign sequence number
	m.BeforeCreate = func(env *orm.Environment, vals orm.Values) error {
		name, _ := vals["name"].(string)
		if name == "" || name == "/" || name == "New" {
			seq, err := orm.NextByCode(env, "purchase.order")
			if err != nil {
				// Fallback: generate a simple name
				vals["name"] = fmt.Sprintf("PO/%d", time.Now().UnixNano()%100000)
			} else {
				vals["name"] = seq
			}
		}
		return nil
	}

	// purchase.order.line — individual line items on a PO
	initPurchaseOrderLine()
}

// initPurchaseOrderLine registers purchase.order.line.
// Mirrors: odoo/addons/purchase/models/purchase_order_line.py
func initPurchaseOrderLine() {
	m := orm.NewModel("purchase.order.line", orm.ModelOpts{
		Description: "Purchase Order Line",
		Order:       "order_id, sequence, id",
	})

	// -- Parent --
	m.AddFields(
		orm.Many2one("order_id", "purchase.order", orm.FieldOpts{
			String: "Order Reference", Required: true, OnDelete: orm.OnDeleteCascade, Index: true,
		}),
		orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 10}),
	)

	// -- Product --
	m.AddFields(
		orm.Many2one("product_id", "product.product", orm.FieldOpts{
			String: "Product", Index: true,
		}),
		orm.Char("name", orm.FieldOpts{String: "Description", Required: true}),
		orm.Float("product_qty", orm.FieldOpts{String: "Quantity", Required: true, Default: 1.0}),
		orm.Many2one("product_uom", "uom.uom", orm.FieldOpts{
			String: "Unit of Measure", Required: true,
		}),
	)

	// -- Pricing --
	m.AddFields(
		orm.Float("price_unit", orm.FieldOpts{String: "Unit Price", Required: true}),
		orm.Many2many("tax_ids", "account.tax", orm.FieldOpts{String: "Taxes"}),
		orm.Float("discount", orm.FieldOpts{String: "Discount (%)", Default: 0.0}),
		orm.Monetary("price_subtotal", orm.FieldOpts{
			String: "Subtotal", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
		}),
		orm.Monetary("price_total", orm.FieldOpts{
			String: "Total", Compute: "_compute_amount", Store: true, CurrencyField: "currency_id",
		}),
		orm.Many2one("currency_id", "res.currency", orm.FieldOpts{
			String: "Currency",
		}),
	)

	// -- Dates --
	m.AddFields(
		orm.Datetime("date_planned", orm.FieldOpts{String: "Expected Arrival"}),
	)

	// -- Quantities --
	m.AddFields(
		orm.Float("qty_received", orm.FieldOpts{
			String: "Received Qty", Compute: "_compute_qty_received", Store: true,
		}),
		orm.Float("qty_invoiced", orm.FieldOpts{
			String: "Billed Qty", Compute: "_compute_qty_invoiced", Store: true,
		}),
		orm.Float("qty_to_invoice", orm.FieldOpts{
			String: "To Invoice Quantity", Compute: "_compute_qty_to_invoice", Store: true,
		}),
	)

	// -- Company --
	m.AddFields(
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Index: true,
		}),
	)
}
