package models

import (
	"fmt"
	"time"

	"odoo-go/pkg/orm"
)

// initSaleReport registers sale.report — a transient model for sales analysis.
// Mirrors: odoo/addons/sale/report/sale_report.py
//
//	class SaleReport(models.Model):
//	    _name = 'sale.report'
//	    _description = 'Sales Analysis Report'
//	    _auto = False
func initSaleReport() {
	m := orm.NewModel("sale.report", orm.ModelOpts{
		Description: "Sales Analysis Report",
		Type:        orm.ModelTransient,
	})

	m.AddFields(
		orm.Date("date_from", orm.FieldOpts{String: "Start Date"}),
		orm.Date("date_to", orm.FieldOpts{String: "End Date"}),
		orm.Many2one("partner_id", "res.partner", orm.FieldOpts{String: "Customer"}),
		orm.Many2one("product_id", "product.product", orm.FieldOpts{String: "Product"}),
		orm.Many2one("product_tmpl_id", "product.template", orm.FieldOpts{String: "Product Template"}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company"}),
		orm.Many2one("user_id", "res.users", orm.FieldOpts{String: "Salesperson"}),
		orm.Many2one("team_id", "crm.team", orm.FieldOpts{String: "Sales Team"}),
		orm.Many2one("categ_id", "product.category", orm.FieldOpts{String: "Product Category"}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "Quotation"},
			{Value: "sale", Label: "Sales Order"},
			{Value: "done", Label: "Done"},
			{Value: "cancel", Label: "Cancelled"},
		}, orm.FieldOpts{String: "Status"}),
	)

	// get_sales_data: Retrieve aggregated sales data for dashboards and reports.
	// Mirrors: odoo/addons/sale/report/sale_report.py (the SQL view query logic)
	// Returns: { months, top_products, top_customers, summary }
	m.RegisterMethod("get_sales_data", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()

		// ── Revenue by month (last 12 months) ──
		monthRows, err := env.Tx().Query(env.Ctx(), `
			SELECT date_trunc('month', so.date_order) AS month,
				COUNT(DISTINCT so.id) AS order_count,
				COALESCE(SUM(so.amount_total::float8), 0) AS revenue,
				COALESCE(AVG(so.amount_total::float8), 0) AS avg_order
			FROM sale_order so
			WHERE so.state IN ('sale', 'done')
			GROUP BY month
			ORDER BY month DESC
			LIMIT 12`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: monthly revenue query: %w", err)
		}
		defer monthRows.Close()

		var months []map[string]interface{}
		for monthRows.Next() {
			var month time.Time
			var cnt int64
			var rev, avg float64
			if err := monthRows.Scan(&month, &cnt, &rev, &avg); err != nil {
				continue
			}
			months = append(months, map[string]interface{}{
				"month":     month.Format("2006-01"),
				"orders":    cnt,
				"revenue":   rev,
				"avg_order": avg,
			})
		}

		// ── Top 10 products by revenue ──
		prodRows, err := env.Tx().Query(env.Ctx(), `
			SELECT pt.name,
				SUM(sol.product_uom_qty) AS qty,
				COALESCE(SUM(sol.price_subtotal::float8), 0) AS revenue,
				COUNT(DISTINCT sol.order_id) AS order_count
			FROM sale_order_line sol
			JOIN sale_order so ON so.id = sol.order_id AND so.state IN ('sale', 'done')
			JOIN product_product pp ON pp.id = sol.product_id
			JOIN product_template pt ON pt.id = pp.product_tmpl_id
			WHERE sol.product_id IS NOT NULL
			GROUP BY pt.name
			ORDER BY revenue DESC
			LIMIT 10`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: top products query: %w", err)
		}
		defer prodRows.Close()

		var products []map[string]interface{}
		for prodRows.Next() {
			var name string
			var qty, rev float64
			var orderCnt int64
			if err := prodRows.Scan(&name, &qty, &rev, &orderCnt); err != nil {
				continue
			}
			products = append(products, map[string]interface{}{
				"product":  name,
				"qty":      qty,
				"revenue":  rev,
				"orders":   orderCnt,
			})
		}

		// ── Top 10 customers by revenue ──
		custRows, err := env.Tx().Query(env.Ctx(), `
			SELECT p.name,
				COUNT(DISTINCT so.id) AS orders,
				COALESCE(SUM(so.amount_total::float8), 0) AS revenue,
				COALESCE(AVG(so.amount_total::float8), 0) AS avg_order
			FROM sale_order so
			JOIN res_partner p ON p.id = so.partner_id
			WHERE so.state IN ('sale', 'done')
			GROUP BY p.id, p.name
			ORDER BY revenue DESC
			LIMIT 10`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: top customers query: %w", err)
		}
		defer custRows.Close()

		var customers []map[string]interface{}
		for custRows.Next() {
			var name string
			var cnt int64
			var rev, avg float64
			if err := custRows.Scan(&name, &cnt, &rev, &avg); err != nil {
				continue
			}
			customers = append(customers, map[string]interface{}{
				"customer":  name,
				"orders":    cnt,
				"revenue":   rev,
				"avg_order": avg,
			})
		}

		// ── Summary totals ──
		var totalOrders int64
		var totalRevenue, avgOrderValue float64
		env.Tx().QueryRow(env.Ctx(), `
			SELECT COUNT(*), COALESCE(SUM(amount_total::float8), 0), COALESCE(AVG(amount_total::float8), 0)
			FROM sale_order WHERE state IN ('sale', 'done')
		`).Scan(&totalOrders, &totalRevenue, &avgOrderValue)

		return map[string]interface{}{
			"months":        months,
			"top_products":  products,
			"top_customers": customers,
			"summary": map[string]interface{}{
				"total_orders":    totalOrders,
				"total_revenue":   totalRevenue,
				"avg_order_value": avgOrderValue,
			},
		}, nil
	})

	// get_sales_by_salesperson: Breakdown by salesperson.
	// Mirrors: odoo/addons/sale/report/sale_report.py (grouped by user_id)
	m.RegisterMethod("get_sales_by_salesperson", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()

		rows, err := env.Tx().Query(env.Ctx(), `
			SELECT COALESCE(rp.name, 'Unassigned') AS salesperson,
				COUNT(DISTINCT so.id) AS orders,
				COALESCE(SUM(so.amount_total::float8), 0) AS revenue,
				COALESCE(AVG(so.amount_total::float8), 0) AS avg_order
			FROM sale_order so
			LEFT JOIN res_users ru ON ru.id = so.user_id
			LEFT JOIN res_partner rp ON rp.id = ru.partner_id
			WHERE so.state IN ('sale', 'done')
			GROUP BY rp.name
			ORDER BY revenue DESC`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: salesperson query: %w", err)
		}
		defer rows.Close()

		var results []map[string]interface{}
		for rows.Next() {
			var name string
			var orders int64
			var rev, avg float64
			if err := rows.Scan(&name, &orders, &rev, &avg); err != nil {
				continue
			}
			results = append(results, map[string]interface{}{
				"salesperson": name,
				"orders":      orders,
				"revenue":     rev,
				"avg_order":   avg,
			})
		}
		return results, nil
	})

	// get_sales_by_category: Breakdown by product category.
	// Mirrors: odoo/addons/sale/report/sale_report.py (grouped by categ_id)
	m.RegisterMethod("get_sales_by_category", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()

		rows, err := env.Tx().Query(env.Ctx(), `
			SELECT COALESCE(pc.name, 'Uncategorized') AS category,
				COUNT(DISTINCT sol.order_id) AS orders,
				SUM(sol.product_uom_qty) AS qty,
				COALESCE(SUM(sol.price_subtotal::float8), 0) AS revenue
			FROM sale_order_line sol
			JOIN sale_order so ON so.id = sol.order_id AND so.state IN ('sale', 'done')
			LEFT JOIN product_product pp ON pp.id = sol.product_id
			LEFT JOIN product_template pt ON pt.id = pp.product_tmpl_id
			LEFT JOIN product_category pc ON pc.id = pt.categ_id
			WHERE sol.product_id IS NOT NULL
			GROUP BY pc.name
			ORDER BY revenue DESC`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: category query: %w", err)
		}
		defer rows.Close()

		var results []map[string]interface{}
		for rows.Next() {
			var name string
			var orders int64
			var qty, rev float64
			if err := rows.Scan(&name, &orders, &qty, &rev); err != nil {
				continue
			}
			results = append(results, map[string]interface{}{
				"category": name,
				"orders":   orders,
				"qty":      qty,
				"revenue":  rev,
			})
		}
		return results, nil
	})

	// get_invoice_analysis: Invoice status analysis.
	// Mirrors: odoo/addons/sale/report/sale_report.py (invoice_status grouping)
	m.RegisterMethod("get_invoice_analysis", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()

		rows, err := env.Tx().Query(env.Ctx(), `
			SELECT COALESCE(invoice_status, 'no') AS status,
				COUNT(*) AS count,
				COALESCE(SUM(amount_total::float8), 0) AS revenue
			FROM sale_order
			WHERE state IN ('sale', 'done')
			GROUP BY invoice_status
			ORDER BY revenue DESC`)
		if err != nil {
			return nil, fmt.Errorf("sale_report: invoice analysis query: %w", err)
		}
		defer rows.Close()

		var results []map[string]interface{}
		for rows.Next() {
			var status string
			var count int64
			var rev float64
			if err := rows.Scan(&status, &count, &rev); err != nil {
				continue
			}
			results = append(results, map[string]interface{}{
				"status":  status,
				"count":   count,
				"revenue": rev,
			})
		}
		return results, nil
	})
}

// initSaleOrderWarnMsg registers the sale.order.onchange.warning transient model.
// Mirrors: odoo/addons/sale/wizard/sale_order_cancel.py (cancel warning dialog)
func initSaleOrderWarnMsg() {
	m := orm.NewModel("sale.order.cancel", orm.ModelOpts{
		Description: "Sale Order Cancel",
		Type:        orm.ModelTransient,
	})

	m.AddFields(
		orm.Many2one("order_id", "sale.order", orm.FieldOpts{String: "Sale Order"}),
		orm.Text("cancel_reason", orm.FieldOpts{String: "Cancellation Reason"}),
	)

	// action_cancel: Confirm the cancellation of the sale order.
	m.RegisterMethod("action_cancel", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		wizID := rs.IDs()[0]
		var orderID int64
		err := env.Tx().QueryRow(env.Ctx(),
			`SELECT order_id FROM sale_order_cancel WHERE id = $1`, wizID).Scan(&orderID)
		if err != nil {
			return nil, fmt.Errorf("sale_cancel: read wizard %d: %w", wizID, err)
		}
		soRS := env.Model("sale.order").Browse(orderID)
		soModel := orm.Registry.Get("sale.order")
		if fn, ok := soModel.Methods["action_cancel"]; ok {
			return fn(soRS)
		}
		return nil, fmt.Errorf("sale_cancel: action_cancel method not found")
	})
}

// initSaleAdvancePaymentWizard registers the sale.advance.payment.inv wizard.
// Mirrors: odoo/addons/sale/wizard/sale_make_invoice_advance.py
//
//	class SaleAdvancePaymentInv(models.TransientModel):
//	    _name = 'sale.advance.payment.inv'
//	    _description = 'Sales Advance Payment Invoice'
func initSaleAdvancePaymentWizard() {
	m := orm.NewModel("sale.advance.payment.inv", orm.ModelOpts{
		Description: "Sales Advance Payment Invoice",
		Type:        orm.ModelTransient,
	})

	m.AddFields(
		orm.Selection("advance_payment_method", []orm.SelectionItem{
			{Value: "delivered", Label: "Regular invoice"},
			{Value: "percentage", Label: "Down payment (percentage)"},
			{Value: "fixed", Label: "Down payment (fixed amount)"},
		}, orm.FieldOpts{String: "Create Invoice", Default: "delivered", Required: true}),
		orm.Float("amount", orm.FieldOpts{String: "Down Payment Amount", Default: 0}),
		orm.Boolean("has_down_payments", orm.FieldOpts{String: "Has down payments"}),
		orm.Boolean("deduct_down_payments", orm.FieldOpts{String: "Deduct down payments", Default: true}),
		orm.Many2one("product_id", "product.product", orm.FieldOpts{String: "Down Payment Product"}),
		orm.Many2one("currency_id", "res.currency", orm.FieldOpts{String: "Currency"}),
		orm.Float("fixed_amount", orm.FieldOpts{String: "Fixed Amount"}),
		orm.Integer("count", orm.FieldOpts{String: "Order Count"}),
		orm.Many2many("sale_order_ids", "sale.order", orm.FieldOpts{String: "Sale Orders"}),
	)

	// create_invoices: Generate invoices based on the wizard settings.
	// Mirrors: odoo/addons/sale/wizard/sale_make_invoice_advance.py create_invoices()
	m.RegisterMethod("create_invoices", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		wizID := rs.IDs()[0]

		// Read wizard settings
		var method string
		var amount, fixedAmount float64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(advance_payment_method, 'delivered'),
				COALESCE(amount, 0), COALESCE(fixed_amount, 0)
			 FROM sale_advance_payment_inv WHERE id = $1`, wizID,
		).Scan(&method, &amount, &fixedAmount)

		// Get linked sale order IDs from the M2M or from context
		soRows, err := env.Tx().Query(env.Ctx(),
			`SELECT sale_order_id FROM sale_order_sale_advance_payment_inv_rel
			 WHERE sale_advance_payment_inv_id = $1`, wizID)
		if err != nil {
			return nil, fmt.Errorf("sale_advance_wiz: read SO IDs: %w", err)
		}
		defer soRows.Close()

		var soIDs []int64
		for soRows.Next() {
			var id int64
			soRows.Scan(&id)
			soIDs = append(soIDs, id)
		}

		if len(soIDs) == 0 {
			return nil, fmt.Errorf("sale_advance_wiz: no sale orders linked")
		}

		soModel := orm.Registry.Get("sale.order")

		switch method {
		case "delivered":
			// Create regular invoices for all linked SOs
			soRS := env.Model("sale.order").Browse(soIDs...)
			if fn, ok := soModel.Methods["create_invoices"]; ok {
				return fn(soRS)
			}
			return nil, fmt.Errorf("sale_advance_wiz: create_invoices method not found")
		case "percentage":
			// Create down payment invoices
			for _, soID := range soIDs {
				soRS := env.Model("sale.order").Browse(soID)
				if fn, ok := soModel.Methods["action_create_down_payment"]; ok {
					if _, err := fn(soRS, amount); err != nil {
						return nil, fmt.Errorf("sale_advance_wiz: down payment for SO %d: %w", soID, err)
					}
				}
			}
			return true, nil
		case "fixed":
			// Create fixed-amount down payment (treat as percentage by computing %)
			for _, soID := range soIDs {
				var total float64
				env.Tx().QueryRow(env.Ctx(),
					`SELECT COALESCE(amount_total::float8, 0) FROM sale_order WHERE id = $1`, soID).Scan(&total)
				pct := float64(0)
				if total > 0 {
					pct = fixedAmount / total * 100
				}
				soRS := env.Model("sale.order").Browse(soID)
				if fn, ok := soModel.Methods["action_create_down_payment"]; ok {
					if _, err := fn(soRS, pct); err != nil {
						return nil, fmt.Errorf("sale_advance_wiz: fixed down payment for SO %d: %w", soID, err)
					}
				}
			}
			return true, nil
		}

		return nil, fmt.Errorf("sale_advance_wiz: unknown method %q", method)
	})
}
