# -*- coding: utf-8 -*-

from odoo import models, fields, api, _
from odoo.exceptions import UserError
from collections import defaultdict


class PurchaseOrderWizard(models.TransientModel):
    _name = 'purchaseorder.wiz'
    _description = "Purchase Order Wizard"

    sale_order_id = fields.Many2one('sale.order', string='Sale Order')
    product_line_ids = fields.One2many(
        'purchase.product.wiz',
        'wizard_id',
        string='Products'
    )
    # Batch vendor assignment - uses onchange to apply immediately
    batch_vendor_id = fields.Many2one(
        'res.partner',
        string='Assign Vendor to All',
        domain="[('is_company', '=', True)]",
        help="Select a vendor here to assign it to ALL products at once"
    )

    @api.onchange('batch_vendor_id')
    def _onchange_batch_vendor_id(self):
        """When user selects a batch vendor, apply it to selected lines only"""
        if self.batch_vendor_id:
            # Get selected lines, or all lines if none selected
            selected_lines = self.product_line_ids.filtered(lambda l: l.selected)
            lines_to_update = selected_lines if selected_lines else self.product_line_ids
            
            for line in lines_to_update:
                line.vendor_id = self.batch_vendor_id
                # Update price from vendor if available
                if line.product_id:
                    seller = line.product_id.seller_ids.filtered(
                        lambda s: s.partner_id.id == self.batch_vendor_id.id
                    )[:1]
                    if seller and seller.price:
                        line.price_unit = seller.price
                        line.price_subtotal = line.price_unit * line.product_uom_qty
            
            # Clear selection after applying
            for line in selected_lines:
                line.selected = False

    @api.model
    def default_get(self, fields_list):
        """Load SO lines into wizard"""
        res = super().default_get(fields_list)
        active_ids = self._context.get('active_ids')
        if not active_ids:
            return res
            
        sale_order = self.env['sale.order'].browse(active_ids[0])
        if not sale_order.order_line:
            raise UserError(_("Please add some valid sale order lines...!"))
        
        res['sale_order_id'] = sale_order.id
        
        # Load all products
        product_lines = []
        for line in sale_order.order_line:
            if line.display_type in ('line_section', 'line_note'):
                continue
            
            # Ensure description is never empty
            description = line.name or line.product_id.display_name or line.product_id.name or 'Product'
            
            product_lines.append((0, 0, {
                'product_id': line.product_id.id,
                'description': description,
                'product_uom_qty': line.product_uom_qty,
                'price_unit': line.price_unit,
                'product_uom': line.product_uom_id.id,
                'price_subtotal': line.price_subtotal,
                'so_line_id': line.id,
                'vendor_id': False,
            }))
        
        res['product_line_ids'] = product_lines
        return res

    def create_po(self):
        """Generate purchase orders grouped by vendor"""
        self.ensure_one()
        
        # Group products by vendor
        vendor_products = defaultdict(list)
        
        for product_line in self.product_line_ids:
            if product_line.vendor_id:
                vendor_products[product_line.vendor_id.id].append(product_line)
        
        if not vendor_products:
            raise UserError(_("Please assign at least one product to a vendor before creating Purchase Orders."))
        
        now = fields.Datetime.now()
        created_pos = self.env['purchase.order']
        
        for vendor_id, product_lines in vendor_products.items():
            partner = self.env['res.partner'].browse(vendor_id)
            fpos = self.env['account.fiscal.position'].sudo()._get_fiscal_position(partner)
            
            # Use SO commitment date or current date for date_planned
            date_planned = self.sale_order_id.commitment_date or now
            
            # Get the customer name for "Marked For" field
            marked_for_name = self.sale_order_id.partner_id.name or self.sale_order_id.partner_id.display_name
            
            # Create the Purchase Order
            purchase_order = self.env['purchase.order'].create({
                'partner_id': partner.id,
                'partner_ref': partner.ref,
                'company_id': self.sale_order_id.company_id.id,
                'currency_id': self.env.company.currency_id.id,
                'dest_address_id': False,
                'origin': self.sale_order_id.name,
                'payment_term_id': partner.property_supplier_payment_term_id.id,
                'date_order': now,
                'fiscal_position_id': fpos.id,
                'sale_ord_id': self.sale_order_id.id,
                'marked_for': marked_for_name,
            })
            
            # Create PO lines
            for product_line in product_lines:
                values = product_line._prepare_purchase_line_values(purchase_order, date_planned)
                self.env['purchase.order.line'].create(values)
            
            created_pos |= purchase_order
        
        # Return action based on number of POs created
        if len(created_pos) == 1:
            return {
                'name': _('Purchase Order'),
                'view_mode': 'form',
                'res_model': 'purchase.order',
                'view_id': self.env.ref('purchase.purchase_order_form').id,
                'res_id': created_pos.id,
                'type': 'ir.actions.act_window',
                'target': 'current',
            }
        else:
            return {
                'name': _('Purchase Orders'),
                'view_mode': 'list,form',
                'res_model': 'purchase.order',
                'domain': [('id', 'in', created_pos.ids)],
                'type': 'ir.actions.act_window',
                'target': 'current',
            }


class PurchaseProductWiz(models.TransientModel):
    _name = 'purchase.product.wiz'
    _description = "Purchase Product Wizard Line"

    wizard_id = fields.Many2one('purchaseorder.wiz', string='Wizard', ondelete='cascade')
    selected = fields.Boolean(string='Select', default=False)
    so_line_id = fields.Many2one('sale.order.line', string='SO Line')
    product_id = fields.Many2one('product.product', string='Product')
    product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
    description = fields.Char(string='Description')
    product_uom_qty = fields.Float(string='Quantity')
    price_unit = fields.Float(string='Unit Price')
    price_subtotal = fields.Float(string='Subtotal')
    vendor_id = fields.Many2one(
        'res.partner',
        string='Vendor',
        domain="[('is_company', '=', True)]"
    )

    @api.onchange('vendor_id')
    def _onchange_vendor_id(self):
        """Update price based on vendor's price list if available"""
        if self.vendor_id and self.product_id:
            seller = self.product_id.seller_ids.filtered(
                lambda s: s.partner_id.id == self.vendor_id.id
            )[:1]
            if seller and seller.price:
                self.price_unit = seller.price
                self.price_subtotal = self.price_unit * self.product_uom_qty

    def _prepare_purchase_line_values(self, purchase_order, date_planned):
        """Returns the values to create the purchase order line."""
        self.ensure_one()
        # Use the wizard's UoM, or fall back to the product's default UoM
        product_uom = self.product_uom or self.product_id.uom_id
        
        # Ensure name is never empty - required field on purchase.order.line
        name = self.description
        if not name:
            name = self.product_id.display_name or self.product_id.name or 'Product'
        
        if self.product_id.default_code and self.product_id.default_code not in name:
            name = '[%s] %s' % (self.product_id.default_code, name)
        
        return {
            'name': name,
            'product_qty': self.product_uom_qty,
            'product_id': self.product_id.id,
            'product_uom_id': product_uom.id,
            'price_unit': self.price_unit,
            'date_planned': date_planned,
            'order_id': purchase_order.id,
        }
