package models

import (
	"fmt"

	"odoo-go/pkg/orm"
)

// initStockBarcode registers stock.barcode.picking — transient model for barcode scanning interface.
// Mirrors: odoo/addons/stock_barcode/models/stock_picking.py barcode processing
func initStockBarcode() {
	m := orm.NewModel("stock.barcode.picking", orm.ModelOpts{
		Description: "Barcode Picking Interface",
		Type:        orm.ModelTransient,
	})
	m.AddFields(
		orm.Many2one("picking_id", "stock.picking", orm.FieldOpts{String: "Transfer"}),
		orm.Char("barcode", orm.FieldOpts{String: "Barcode"}),
	)

	// process_barcode: Look up a product or lot/serial by barcode.
	// Mirrors: stock_barcode barcode scanning logic
	m.RegisterMethod("process_barcode", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		env := rs.Env()
		if len(args) < 1 {
			return nil, fmt.Errorf("barcode required")
		}
		barcode, _ := args[0].(string)

		// Try to find product by barcode
		var productID int64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT pp.id FROM product_product pp
             JOIN product_template pt ON pt.id = pp.product_tmpl_id
             WHERE pt.barcode = $1 OR pp.barcode = $1 LIMIT 1`, barcode,
		).Scan(&productID)

		if productID > 0 {
			return map[string]interface{}{"product_id": productID, "found": true}, nil
		}

		// Try lot/serial
		var lotID, lotProductID int64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT id, product_id FROM stock_lot WHERE name = $1 LIMIT 1`, barcode,
		).Scan(&lotID, &lotProductID)

		if lotID > 0 {
			return map[string]interface{}{"lot_id": lotID, "product_id": lotProductID, "found": true}, nil
		}

		// Try package
		var packageID int64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT id FROM stock_quant_package WHERE name = $1 LIMIT 1`, barcode,
		).Scan(&packageID)

		if packageID > 0 {
			return map[string]interface{}{"package_id": packageID, "found": true}, nil
		}

		// Try location barcode
		var locationID int64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT id FROM stock_location WHERE barcode = $1 LIMIT 1`, barcode,
		).Scan(&locationID)

		if locationID > 0 {
			return map[string]interface{}{"location_id": locationID, "found": true}, nil
		}

		return map[string]interface{}{"found": false, "barcode": barcode}, nil
	})

	// process_barcode_picking: Process a barcode in the context of a picking.
	// Finds the product and increments qty_done on the matching move line.
	// Mirrors: stock_barcode.picking barcode processing
	m.RegisterMethod("process_barcode_picking", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		if len(args) < 2 {
			return nil, fmt.Errorf("stock.barcode.picking.process_barcode_picking requires picking_id, barcode")
		}
		pickingID, _ := args[0].(int64)
		barcode, _ := args[1].(string)

		if pickingID == 0 || barcode == "" {
			return nil, fmt.Errorf("stock.barcode.picking: invalid picking_id or barcode")
		}

		env := rs.Env()

		// Find product by barcode
		var productID int64
		env.Tx().QueryRow(env.Ctx(),
			`SELECT pp.id FROM product_product pp
             JOIN product_template pt ON pt.id = pp.product_tmpl_id
             WHERE pt.barcode = $1 OR pp.barcode = $1 LIMIT 1`, barcode,
		).Scan(&productID)

		if productID == 0 {
			// Try lot/serial
			var lotProductID int64
			env.Tx().QueryRow(env.Ctx(),
				`SELECT product_id FROM stock_lot WHERE name = $1 LIMIT 1`, barcode,
			).Scan(&lotProductID)
			productID = lotProductID
		}

		if productID == 0 {
			return map[string]interface{}{
				"found": false, "barcode": barcode,
				"message": fmt.Sprintf("No product found for barcode %q", barcode),
			}, nil
		}

		// Find matching move line
		var moveLineID int64
		err := env.Tx().QueryRow(env.Ctx(),
			`SELECT sml.id FROM stock_move_line sml
			 JOIN stock_move sm ON sm.id = sml.move_id
			 WHERE sm.picking_id = $1 AND sml.product_id = $2 AND sm.state != 'done'
			 ORDER BY sml.id LIMIT 1`,
			pickingID, productID,
		).Scan(&moveLineID)

		if err != nil || moveLineID == 0 {
			// No existing move line — check if there is a move for this product
			var moveID int64
			env.Tx().QueryRow(env.Ctx(),
				`SELECT id FROM stock_move WHERE picking_id = $1 AND product_id = $2 AND state != 'done' LIMIT 1`,
				pickingID, productID,
			).Scan(&moveID)

			if moveID == 0 {
				return map[string]interface{}{
					"found": false, "product_id": productID,
					"message": fmt.Sprintf("Product %d not expected in this transfer", productID),
				}, nil
			}

			return map[string]interface{}{
				"found": true, "product_id": productID, "move_id": moveID,
				"action": "new_line",
				"message": "Product found in move, new line needed",
			}, nil
		}

		// Increment quantity on the move line
		_, err = env.Tx().Exec(env.Ctx(),
			`UPDATE stock_move_line SET quantity = quantity + 1 WHERE id = $1`, moveLineID)
		if err != nil {
			return nil, fmt.Errorf("stock.barcode.picking: increment qty on move line %d: %w", moveLineID, err)
		}

		return map[string]interface{}{
			"found": true, "product_id": productID, "move_line_id": moveLineID,
			"action": "incremented", "message": "Quantity incremented",
		}, nil
	})

	// get_picking_data: Return the full picking data for the barcode interface.
	// Mirrors: stock_barcode.picking get_barcode_data()
	m.RegisterMethod("get_picking_data", func(rs *orm.Recordset, args ...interface{}) (interface{}, error) {
		if len(args) < 1 {
			return nil, fmt.Errorf("stock.barcode.picking.get_picking_data requires picking_id")
		}
		pickingID, _ := args[0].(int64)
		if pickingID == 0 {
			return nil, fmt.Errorf("stock.barcode.picking: invalid picking_id")
		}

		env := rs.Env()

		// Get picking header
		var pickingName, state string
		var srcLocID, dstLocID int64
		err := env.Tx().QueryRow(env.Ctx(),
			`SELECT name, state, location_id, location_dest_id
			 FROM stock_picking WHERE id = $1`, pickingID,
		).Scan(&pickingName, &state, &srcLocID, &dstLocID)
		if err != nil {
			return nil, fmt.Errorf("stock.barcode.picking: read picking %d: %w", pickingID, err)
		}

		// Get source/dest location names
		var srcLocName, dstLocName string
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(complete_name, name) FROM stock_location WHERE id = $1`, srcLocID,
		).Scan(&srcLocName)
		env.Tx().QueryRow(env.Ctx(),
			`SELECT COALESCE(complete_name, name) FROM stock_location WHERE id = $1`, dstLocID,
		).Scan(&dstLocName)

		// Get move lines
		rows, err := env.Tx().Query(env.Ctx(),
			`SELECT sm.id, sm.product_id, pt.name as product_name,
			    sm.product_uom_qty as demand,
			    COALESCE(SUM(sml.quantity), 0) as done_qty
			 FROM stock_move sm
			 JOIN product_product pp ON pp.id = sm.product_id
			 JOIN product_template pt ON pt.id = pp.product_tmpl_id
			 LEFT JOIN stock_move_line sml ON sml.move_id = sm.id
			 WHERE sm.picking_id = $1 AND sm.state != 'cancel'
			 GROUP BY sm.id, sm.product_id, pt.name, sm.product_uom_qty
			 ORDER BY pt.name`,
			pickingID,
		)
		if err != nil {
			return nil, fmt.Errorf("stock.barcode.picking: query moves for %d: %w", pickingID, err)
		}
		defer rows.Close()

		var moveLines []map[string]interface{}
		for rows.Next() {
			var moveID, prodID int64
			var prodName string
			var demand, doneQty float64
			if err := rows.Scan(&moveID, &prodID, &prodName, &demand, &doneQty); err != nil {
				return nil, fmt.Errorf("stock.barcode.picking: scan move: %w", err)
			}
			moveLines = append(moveLines, map[string]interface{}{
				"move_id": moveID, "product_id": prodID, "product": prodName,
				"demand": demand, "done": doneQty,
				"remaining": demand - doneQty,
			})
		}

		return map[string]interface{}{
			"picking_id":       pickingID,
			"name":             pickingName,
			"state":            state,
			"source_location":  srcLocName,
			"dest_location":    dstLocName,
			"lines":            moveLines,
		}, nil
	})
}
