package models

import (
	"fmt"
	"time"

	"odoo-go/pkg/orm"
)

// initStock registers all stock models.
// Mirrors: odoo/addons/stock/models/stock_warehouse.py,
//          stock_location.py, stock_picking.py, stock_move.py,
//          stock_move_line.py, stock_quant.py, stock_lot.py

func initStock() {
	initStockWarehouse()
	initStockLocation()
	initStockPickingType()
	initStockPicking()
	initStockMove()
	initStockMoveLine()
	initStockQuant()
	initStockLot()
}

// initStockWarehouse registers stock.warehouse.
// Mirrors: odoo/addons/stock/models/stock_warehouse.py
func initStockWarehouse() {
	m := orm.NewModel("stock.warehouse", orm.ModelOpts{
		Description: "Warehouse",
		Order:       "sequence, id",
		RecName:     "name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Warehouse", Required: true, Index: true}),
		orm.Char("code", orm.FieldOpts{String: "Short Name", Required: true, Size: 5}),
		orm.Boolean("active", orm.FieldOpts{String: "Active", Default: true}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Many2one("partner_id", "res.partner", orm.FieldOpts{
			String: "Address",
		}),
		orm.Many2one("lot_stock_id", "stock.location", orm.FieldOpts{
			String: "Location Stock", Required: true, OnDelete: orm.OnDeleteRestrict,
		}),
		orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 10}),
	)
}

// initStockLocation registers stock.location.
// Mirrors: odoo/addons/stock/models/stock_location.py
func initStockLocation() {
	m := orm.NewModel("stock.location", orm.ModelOpts{
		Description: "Location",
		Order:       "complete_name, id",
		RecName:     "complete_name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Location Name", Required: true, Translate: true}),
		orm.Char("complete_name", orm.FieldOpts{
			String: "Full Location Name", Compute: "_compute_complete_name", Store: true,
		}),
		orm.Boolean("active", orm.FieldOpts{String: "Active", Default: true}),
		orm.Selection("usage", []orm.SelectionItem{
			{Value: "supplier", Label: "Vendor Location"},
			{Value: "view", Label: "View"},
			{Value: "internal", Label: "Internal Location"},
			{Value: "customer", Label: "Customer Location"},
			{Value: "inventory", Label: "Inventory Loss"},
			{Value: "production", Label: "Production"},
			{Value: "transit", Label: "Transit Location"},
		}, orm.FieldOpts{String: "Location Type", Required: true, Default: "internal", Index: true}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "Parent Location", Index: true, OnDelete: orm.OnDeleteCascade,
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Index: true,
		}),
		orm.Many2one("removal_strategy_id", "product.removal", orm.FieldOpts{
			String: "Removal Strategy",
		}),
		orm.Boolean("scrap_location", orm.FieldOpts{String: "Is a Scrap Location?"}),
		orm.Boolean("return_location", orm.FieldOpts{String: "Is a Return Location?"}),
		orm.Char("barcode", orm.FieldOpts{String: "Barcode", Index: true}),
	)
}

// initStockPickingType registers stock.picking.type.
// Mirrors: odoo/addons/stock/models/stock_picking.py StockPickingType
func initStockPickingType() {
	m := orm.NewModel("stock.picking.type", orm.ModelOpts{
		Description: "Picking Type",
		Order:       "sequence, id",
		RecName:     "name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Operation Type", Required: true, Translate: true}),
		orm.Selection("code", []orm.SelectionItem{
			{Value: "incoming", Label: "Receipt"},
			{Value: "outgoing", Label: "Delivery"},
			{Value: "internal", Label: "Internal Transfer"},
		}, orm.FieldOpts{String: "Type of Operation", Required: true}),
		orm.Char("sequence_code", orm.FieldOpts{String: "Code", Required: true}),
		orm.Boolean("active", orm.FieldOpts{String: "Active", Default: true}),
		orm.Many2one("warehouse_id", "stock.warehouse", orm.FieldOpts{
			String: "Warehouse", OnDelete: orm.OnDeleteCascade,
		}),
		orm.Many2one("default_location_src_id", "stock.location", orm.FieldOpts{
			String: "Default Source Location",
		}),
		orm.Many2one("default_location_dest_id", "stock.location", orm.FieldOpts{
			String: "Default Destination Location",
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 10}),
		orm.Boolean("show_operations", orm.FieldOpts{String: "Show Detailed Operations"}),
		orm.Boolean("show_reserved", orm.FieldOpts{String: "Show Reserved"}),
	)
}

// initStockPicking registers stock.picking — the transfer order.
// Mirrors: odoo/addons/stock/models/stock_picking.py StockPicking
func initStockPicking() {
	m := orm.NewModel("stock.picking", orm.ModelOpts{
		Description: "Transfer",
		Order:       "priority desc, scheduled_date asc, id desc",
		RecName:     "name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{
			String: "Reference", Default: "/", Required: true, Index: true, Readonly: true,
		}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "Draft"},
			{Value: "waiting", Label: "Waiting Another Operation"},
			{Value: "confirmed", Label: "Waiting"},
			{Value: "assigned", Label: "Ready"},
			{Value: "done", Label: "Done"},
			{Value: "cancel", Label: "Cancelled"},
		}, orm.FieldOpts{String: "Status", Default: "draft", Compute: "_compute_state", Store: true, Index: true}),
		orm.Selection("priority", []orm.SelectionItem{
			{Value: "0", Label: "Normal"},
			{Value: "1", Label: "Urgent"},
		}, orm.FieldOpts{String: "Priority", Default: "0", Index: true}),
		orm.Many2one("picking_type_id", "stock.picking.type", orm.FieldOpts{
			String: "Operation Type", Required: true, Index: true,
		}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "Source Location", Required: true, Index: true,
		}),
		orm.Many2one("location_dest_id", "stock.location", orm.FieldOpts{
			String: "Destination Location", Required: true, Index: true,
		}),
		orm.Many2one("partner_id", "res.partner", orm.FieldOpts{
			String: "Contact", Index: true,
		}),
		orm.Datetime("scheduled_date", orm.FieldOpts{String: "Scheduled Date", Required: true, Index: true}),
		orm.Datetime("date_deadline", orm.FieldOpts{String: "Deadline"}),
		orm.Datetime("date_done", orm.FieldOpts{String: "Date of Transfer", Readonly: true}),
		orm.One2many("move_ids", "stock.move", "picking_id", orm.FieldOpts{
			String: "Stock Moves",
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Text("note", orm.FieldOpts{String: "Notes"}),
		orm.Char("origin", orm.FieldOpts{String: "Source Document", Index: true}),
	)

	// --- BeforeCreate hook: auto-generate picking reference ---
	// Mirrors: stock.picking._create_sequence() / ir.sequence
	m.BeforeCreate = func(env *orm.Environment, vals orm.Values) error {
		name, _ := vals["name"].(string)
		if name == "" || name == "/" {
			vals["name"] = fmt.Sprintf("WH/IN/%05d", time.Now().UnixNano()%100000)
		}
		return nil
	}

	// --- Business methods: stock move workflow ---

	// action_confirm transitions a picking from draft → confirmed.
	// Confirms all associated stock moves that are still in draft.
	// Mirrors: stock.picking.action_confirm()
	m.RegisterMethod("action_confirm", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			var state string
			err := env.Tx().QueryRow(env.Ctx(),
				`SELECT state FROM stock_picking WHERE id = $1`, id).Scan(&state)
			if err != nil {
				return nil, fmt.Errorf("stock: cannot read picking %d: %w", id, err)
			}
			if state != "draft" {
				return nil, fmt.Errorf("stock: can only confirm draft pickings (picking %d is %q)", id, state)
			}
			_, err = env.Tx().Exec(env.Ctx(),
				`UPDATE stock_picking SET state = 'confirmed' WHERE id = $1`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: confirm picking %d: %w", id, err)
			}
			// Also confirm all draft moves on this picking
			_, err = env.Tx().Exec(env.Ctx(),
				`UPDATE stock_move SET state = 'confirmed' WHERE picking_id = $1 AND state = 'draft'`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: confirm moves for picking %d: %w", id, err)
			}
		}
		return true, nil
	})

	// action_assign transitions a picking from confirmed → assigned (reserve stock).
	// Mirrors: stock.picking.action_assign()
	m.RegisterMethod("action_assign", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			_, err := env.Tx().Exec(env.Ctx(),
				`UPDATE stock_picking SET state = 'assigned' WHERE id = $1`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: assign picking %d: %w", id, err)
			}
			_, err = env.Tx().Exec(env.Ctx(),
				`UPDATE stock_move SET state = 'assigned' WHERE picking_id = $1 AND state IN ('confirmed', 'partially_available')`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: assign moves for picking %d: %w", id, err)
			}
		}
		return true, nil
	})

	// button_validate transitions a picking from assigned → done (process the transfer).
	// Updates all moves to done and adjusts stock quants (source decremented, dest incremented).
	// Mirrors: stock.picking.button_validate()
	m.RegisterMethod("button_validate", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		for _, id := range rs.IDs() {
			// Mark picking as done
			_, err := env.Tx().Exec(env.Ctx(),
				`UPDATE stock_picking SET state = 'done', date_done = NOW() WHERE id = $1`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: validate picking %d: %w", id, err)
			}
			// Mark all moves as done
			_, err = env.Tx().Exec(env.Ctx(),
				`UPDATE stock_move SET state = 'done', date = NOW() WHERE picking_id = $1`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: validate moves for picking %d: %w", id, err)
			}

			// Update quants: for each done move, adjust source and destination locations
			rows, err := env.Tx().Query(env.Ctx(),
				`SELECT product_id, product_uom_qty, location_id, location_dest_id
				 FROM stock_move WHERE picking_id = $1 AND state = 'done'`, id)
			if err != nil {
				return nil, fmt.Errorf("stock: read moves for picking %d: %w", id, err)
			}

			type moveInfo struct {
				productID int64
				qty       float64
				srcLoc    int64
				dstLoc    int64
			}
			var moves []moveInfo
			for rows.Next() {
				var mi moveInfo
				if err := rows.Scan(&mi.productID, &mi.qty, &mi.srcLoc, &mi.dstLoc); err != nil {
					rows.Close()
					return nil, fmt.Errorf("stock: scan move for picking %d: %w", id, err)
				}
				moves = append(moves, mi)
			}
			rows.Close()
			if err := rows.Err(); err != nil {
				return nil, fmt.Errorf("stock: iterate moves for picking %d: %w", id, err)
			}

			for _, mi := range moves {
				// Decrease source location quant
				if err := updateQuant(env, mi.productID, mi.srcLoc, -mi.qty); err != nil {
					return nil, fmt.Errorf("stock: update source quant for picking %d: %w", id, err)
				}
				// Increase destination location quant
				if err := updateQuant(env, mi.productID, mi.dstLoc, mi.qty); err != nil {
					return nil, fmt.Errorf("stock: update dest quant for picking %d: %w", id, err)
				}
			}
		}
		return true, nil
	})
}

// updateQuant adjusts the on-hand quantity for a product at a location.
// If no quant row exists yet it inserts one; otherwise it updates in place.
func updateQuant(env *orm.Environment, productID, locationID int64, delta float64) error {
	var exists bool
	err := env.Tx().QueryRow(env.Ctx(),
		`SELECT EXISTS(SELECT 1 FROM stock_quant WHERE product_id = $1 AND location_id = $2)`,
		productID, locationID).Scan(&exists)
	if err != nil {
		return err
	}
	if exists {
		_, err = env.Tx().Exec(env.Ctx(),
			`UPDATE stock_quant SET quantity = quantity + $1 WHERE product_id = $2 AND location_id = $3`,
			delta, productID, locationID)
	} else {
		_, err = env.Tx().Exec(env.Ctx(),
			`INSERT INTO stock_quant (product_id, location_id, quantity, company_id) VALUES ($1, $2, $3, 1)`,
			productID, locationID, delta)
	}
	return err
}

// initStockMove registers stock.move — individual product movements.
// Mirrors: odoo/addons/stock/models/stock_move.py
func initStockMove() {
	m := orm.NewModel("stock.move", orm.ModelOpts{
		Description: "Stock Move",
		Order:       "sequence, id",
		RecName:     "name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Description", Required: true}),
		orm.Char("reference", orm.FieldOpts{String: "Reference", Index: true}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "New"},
			{Value: "confirmed", Label: "Waiting Availability"},
			{Value: "partially_available", Label: "Partially Available"},
			{Value: "assigned", Label: "Available"},
			{Value: "done", Label: "Done"},
			{Value: "cancel", Label: "Cancelled"},
		}, orm.FieldOpts{String: "Status", Default: "draft", Index: true}),
		orm.Selection("priority", []orm.SelectionItem{
			{Value: "0", Label: "Normal"},
			{Value: "1", Label: "Urgent"},
		}, orm.FieldOpts{String: "Priority", Default: "0"}),
		orm.Many2one("product_id", "product.product", orm.FieldOpts{
			String: "Product", Required: true, Index: true,
		}),
		orm.Float("product_uom_qty", orm.FieldOpts{String: "Demand", Required: true, Default: 1.0}),
		orm.Many2one("product_uom", "uom.uom", orm.FieldOpts{
			String: "UoM", Required: true,
		}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "Source Location", Required: true, Index: true,
		}),
		orm.Many2one("location_dest_id", "stock.location", orm.FieldOpts{
			String: "Destination Location", Required: true, Index: true,
		}),
		orm.Many2one("picking_id", "stock.picking", orm.FieldOpts{
			String: "Transfer", Index: true,
		}),
		orm.Datetime("date", orm.FieldOpts{String: "Date Scheduled", Required: true, Index: true}),
		orm.Datetime("date_deadline", orm.FieldOpts{String: "Deadline"}),
		orm.Float("price_unit", orm.FieldOpts{String: "Unit Price"}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 10}),
		orm.Char("origin", orm.FieldOpts{String: "Source Document"}),
	)
}

// initStockMoveLine registers stock.move.line — detailed operations per lot/package.
// Mirrors: odoo/addons/stock/models/stock_move_line.py
func initStockMoveLine() {
	m := orm.NewModel("stock.move.line", orm.ModelOpts{
		Description: "Product Moves (Stock Move Line)",
		Order:       "result_package_id desc, id",
	})

	m.AddFields(
		orm.Many2one("move_id", "stock.move", orm.FieldOpts{
			String: "Stock Move", Index: true, OnDelete: orm.OnDeleteCascade,
		}),
		orm.Many2one("product_id", "product.product", orm.FieldOpts{
			String: "Product", Required: true, Index: true,
		}),
		orm.Float("quantity", orm.FieldOpts{String: "Quantity", Required: true, Default: 0.0}),
		orm.Many2one("product_uom_id", "uom.uom", orm.FieldOpts{
			String: "Unit of Measure", Required: true,
		}),
		orm.Many2one("lot_id", "stock.lot", orm.FieldOpts{
			String: "Lot/Serial Number", Index: true,
		}),
		orm.Many2one("package_id", "stock.quant.package", orm.FieldOpts{
			String: "Source Package",
		}),
		orm.Many2one("result_package_id", "stock.quant.package", orm.FieldOpts{
			String: "Destination Package",
		}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "From", Required: true, Index: true,
		}),
		orm.Many2one("location_dest_id", "stock.location", orm.FieldOpts{
			String: "To", Required: true, Index: true,
		}),
		orm.Many2one("picking_id", "stock.picking", orm.FieldOpts{
			String: "Transfer", Index: true,
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Datetime("date", orm.FieldOpts{String: "Date", Required: true}),
		orm.Selection("state", []orm.SelectionItem{
			{Value: "draft", Label: "New"},
			{Value: "confirmed", Label: "Waiting"},
			{Value: "assigned", Label: "Reserved"},
			{Value: "done", Label: "Done"},
			{Value: "cancel", Label: "Cancelled"},
		}, orm.FieldOpts{String: "Status", Default: "draft"}),
	)
}

// initStockQuant registers stock.quant — on-hand inventory quantities.
// Mirrors: odoo/addons/stock/models/stock_quant.py
func initStockQuant() {
	m := orm.NewModel("stock.quant", orm.ModelOpts{
		Description: "Quants",
		Order:       "removal_date, in_date, id",
	})

	m.AddFields(
		orm.Many2one("product_id", "product.product", orm.FieldOpts{
			String: "Product", Required: true, Index: true, OnDelete: orm.OnDeleteRestrict,
		}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "Location", Required: true, Index: true, OnDelete: orm.OnDeleteRestrict,
		}),
		orm.Many2one("lot_id", "stock.lot", orm.FieldOpts{
			String: "Lot/Serial Number", Index: true,
		}),
		orm.Float("quantity", orm.FieldOpts{String: "Quantity", Required: true, Default: 0.0}),
		orm.Float("reserved_quantity", orm.FieldOpts{String: "Reserved Quantity", Required: true, Default: 0.0}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Datetime("in_date", orm.FieldOpts{String: "Incoming Date", Index: true}),
		orm.Many2one("package_id", "stock.quant.package", orm.FieldOpts{
			String: "Package",
		}),
		orm.Many2one("owner_id", "res.partner", orm.FieldOpts{
			String: "Owner",
		}),
		orm.Datetime("removal_date", orm.FieldOpts{String: "Removal Date"}),
	)

	// stock.quant.package — physical packages / containers
	orm.NewModel("stock.quant.package", orm.ModelOpts{
		Description: "Packages",
		Order:       "name",
	}).AddFields(
		orm.Char("name", orm.FieldOpts{String: "Package Reference", Required: true, Index: true}),
		orm.Many2one("package_type_id", "stock.package.type", orm.FieldOpts{
			String: "Package Type",
		}),
		orm.Many2one("location_id", "stock.location", orm.FieldOpts{
			String: "Location", Index: true,
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Index: true,
		}),
		orm.Many2one("owner_id", "res.partner", orm.FieldOpts{
			String: "Owner",
		}),
	)

	// stock.package.type — packaging types (box, pallet, etc.)
	orm.NewModel("stock.package.type", orm.ModelOpts{
		Description: "Package Type",
		Order:       "sequence, id",
	}).AddFields(
		orm.Char("name", orm.FieldOpts{String: "Package Type", Required: true}),
		orm.Integer("sequence", orm.FieldOpts{String: "Sequence", Default: 1}),
		orm.Float("height", orm.FieldOpts{String: "Height"}),
		orm.Float("width", orm.FieldOpts{String: "Width"}),
		orm.Float("packaging_length", orm.FieldOpts{String: "Length"}),
		orm.Float("max_weight", orm.FieldOpts{String: "Max Weight"}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{String: "Company"}),
	)

	// product.removal — removal strategies (FIFO, LIFO, etc.)
	orm.NewModel("product.removal", orm.ModelOpts{
		Description: "Removal Strategy",
		Order:       "name",
	}).AddFields(
		orm.Char("name", orm.FieldOpts{String: "Name", Required: true, Translate: true}),
		orm.Char("method", orm.FieldOpts{String: "Method", Required: true}),
	)
}

// initStockLot registers stock.lot — lot/serial number tracking.
// Mirrors: odoo/addons/stock/models/stock_lot.py
func initStockLot() {
	m := orm.NewModel("stock.lot", orm.ModelOpts{
		Description: "Lot/Serial",
		Order:       "name, id",
		RecName:     "name",
	})

	m.AddFields(
		orm.Char("name", orm.FieldOpts{String: "Lot/Serial Number", Required: true, Index: true}),
		orm.Many2one("product_id", "product.product", orm.FieldOpts{
			String: "Product", Required: true, Index: true, OnDelete: orm.OnDeleteRestrict,
		}),
		orm.Many2one("company_id", "res.company", orm.FieldOpts{
			String: "Company", Required: true, Index: true,
		}),
		orm.Text("note", orm.FieldOpts{String: "Description"}),
	)
}
