import logging

from odoo import fields
from odoo.http import request

_logger = logging.getLogger(__name__)


def _resolve_pricelist(self, request_obj=None):
    try:
        req = request_obj or request
        env = req.env
        website = req.website
    except RuntimeError:
        env = getattr(self, "env", None) or self.env
        website = env["website"].get_current_website()
    pricelist = None
    try:
        param_value = (
            env["ir.config_parameter"]
            .sudo()
            .get_param("website_sale_aplicoop.pricelist_id")
        )
        if param_value:
            pricelist = (
                env["product.pricelist"].browse(int(param_value)).exists() or None
            )
    except Exception as e:
        _logger.warning("_resolve_pricelist: error reading config param: %s", e)

    if not pricelist:
        try:
            pricelist = website._get_current_pricelist()
        except Exception as e:
            _logger.warning(
                "_resolve_pricelist: fallback to website pricelist failed: %s", e
            )

    if not pricelist:
        pricelist = env["product.pricelist"].sudo().search([], limit=1)

    return pricelist


def _prepare_product_display_info(self, product, product_price_info, request_obj=None):
    price_data = product_price_info.get(product.id, {})
    price = (
        price_data.get("price", product.list_price)
        if price_data
        else product.list_price
    )
    price_safe = float(price) if price else 0.0

    uom_category_name = ""
    quantity_step = 1
    price_unit_suffix = ""
    bulk_unit_suffixes = {
        "uom.product_uom_categ_kgm": "/Kg",
        "uom.product_uom_categ_vol": "/L",
    }

    if product.uom_id:
        uom = product.uom_id.sudo()
        if uom.category_id:
            uom_category_name = uom.category_id.sudo().name or ""
            try:
                try:
                    req = request_obj or request
                    ir_model_data = req.env["ir.model.data"].sudo()
                except RuntimeError:
                    ir_model_data = product.env["ir.model.data"].sudo()
                external_id = ir_model_data.search(
                    [
                        ("model", "=", "uom.category"),
                        ("res_id", "=", uom.category_id.id),
                    ],
                    limit=1,
                )
                if external_id:
                    fractional_categories = [
                        "uom.product_uom_categ_kgm",
                        "uom.product_uom_categ_vol",
                        "uom.uom_categ_length",
                        "uom.uom_categ_surface",
                    ]
                    full_xmlid = f"{external_id.module}.{external_id.name}"
                    if full_xmlid in fractional_categories:
                        quantity_step = 0.1
                    price_unit_suffix = bulk_unit_suffixes.get(full_xmlid, "")
            except Exception as e:
                _logger.warning(
                    "_prepare_product_display_info: Error detecting UoM category XML ID for product %s: %s",
                    product.id,
                    str(e),
                )

    base_unit_price = 0.0
    if product.base_unit_count and price_safe:
        base_unit_price = price_safe / product.base_unit_count

    try:
        req = request_obj or request
        tr_env = req.env
    except RuntimeError:
        tr_env = product.env
    out_of_stock_label = tr_env._("Out of stock")
    add_to_cart_label = tr_env._(
        "Add %(product_name)s to cart", product_name=product.name
    )

    return {
        "display_price": price_safe,
        "safe_uom_category": uom_category_name,
        "quantity_step": quantity_step,
        "price_unit_suffix": price_unit_suffix,
        "base_unit_price": base_unit_price,
        "out_of_stock_label": out_of_stock_label,
        "add_to_cart_label": add_to_cart_label,
    }


def _get_pricing_info(
    self,
    product,
    pricelist,
    quantity=1.0,
    partner=None,
    request_obj=None,
):
    req = request_obj or request
    try:
        env = req.env
        website = req.website
    except RuntimeError:
        env = product.env
        website = env["website"].get_current_website()

    partner = partner or env.user.partner_id
    currency = pricelist.currency_id
    website_company = (
        website.company_id
        if website and getattr(website, "company_id", False)
        else False
    )
    company = website_company or product.company_id or env.company

    price, rule_id = pricelist._get_product_price_rule(
        product=product, quantity=quantity, target_currency=currency
    )
    price_before_discount = price
    pricelist_item = env["product.pricelist.item"].sudo().browse(rule_id)
    if pricelist_item and pricelist_item._show_discount_on_shop():
        price_before_discount = pricelist_item._compute_price_before_discount(
            product=product,
            quantity=quantity or 1.0,
            date=fields.Date.context_today(pricelist),
            uom=product.uom_id,
            currency=currency,
        )

    has_discounted_price = price_before_discount > price

    fiscal_position = (
        website.fiscal_position_id.sudo()
        if website and getattr(website, "fiscal_position_id", False)
        else env["account.fiscal.position"].sudo()
    )
    product_taxes = product.sudo().taxes_id._filter_taxes_by_company(company)
    taxes = fiscal_position.map_tax(product_taxes) if product_taxes else product_taxes
    tax_display = "total_included"

    def compute_display(amount):
        if not taxes:
            return amount
        return taxes.compute_all(amount, currency, 1, product, partner)[tax_display]

    display_price = compute_display(price)
    display_list_price = compute_display(price_before_discount)

    return {
        "price_unit": price,
        "price": display_price,
        "list_price": display_list_price,
        "has_discounted_price": has_discounted_price,
        "discount": display_list_price - display_price,
        "tax_included": tax_display == "total_included",
    }


def _compute_price_info(self, products, pricelist, request_obj=None):
    product_price_info = {}

    def _tax_included_default(product_record):
        try:
            req = request_obj or request
            return req.website.show_line_subtotals_tax_selection != "tax_excluded"
        except RuntimeError:
            website = product_record.env["website"].get_current_website()
            if not website:
                return True
            return website.show_line_subtotals_tax_selection != "tax_excluded"

    for product in products:
        product_variant = (
            product.product_variant_ids[0] if product.product_variant_ids else False
        )
        if product_variant and pricelist:
            try:
                try:
                    req = request_obj or request
                    partner = req.env.user.partner_id
                except RuntimeError:
                    partner = product_variant.env.user.partner_id

                pricing = _get_pricing_info(
                    self,
                    product_variant,
                    pricelist,
                    quantity=1.0,
                    partner=partner,
                    request_obj=request_obj,
                )
                product_price_info[product.id] = pricing
            except Exception as e:
                _logger.warning(
                    "_compute_price_info: Error getting price for product %s (id=%s): %s. Using list_price fallback.",
                    product.name,
                    product.id,
                    str(e),
                )
                product_price_info[product.id] = {
                    "price_unit": product.list_price,
                    "price": product.list_price,
                    "list_price": product.list_price,
                    "has_discounted_price": False,
                    "discount": 0.0,
                    "tax_included": _tax_included_default(product),
                }
        else:
            product_price_info[product.id] = {
                "price_unit": product.list_price,
                "price": product.list_price,
                "list_price": product.list_price,
                "has_discounted_price": False,
                "discount": 0.0,
                "tax_included": _tax_included_default(product),
            }
    return product_price_info


def _get_product_supplier_info(self, products):
    product_supplier_info = {}
    for product in products:
        supplier_name = ""
        if product.seller_ids:
            partner = product.seller_ids[0].partner_id.sudo()
            supplier_name = partner.comercial or partner.name or ""
            if partner.city:
                supplier_name += f" ({partner.city})"
        product_supplier_info[product.id] = supplier_name
    return product_supplier_info


def _get_delivery_product_display_price(
    self, delivery_product, pricelist=None, request_obj=None
):
    if not delivery_product:
        return 5.74
    try:
        base_price = float(delivery_product.list_price or 0.0)
        try:
            req = request_obj or request
            website = req.website
            partner = req.env.user.partner_id
            company = (
                website.company_id or delivery_product.company_id or req.env.company
            )
        except RuntimeError:
            env = delivery_product.env
            website = env["website"].get_current_website()
            partner = env.user.partner_id
            company = website.company_id or delivery_product.company_id or env.company

        product_taxes = delivery_product.sudo().taxes_id._filter_taxes_by_company(
            company
        )
        fiscal_position = (
            website.fiscal_position_id.sudo()
            if website and getattr(website, "fiscal_position_id", False)
            else delivery_product.env["account.fiscal.position"]
        )
        taxes = (
            fiscal_position.map_tax(product_taxes) if product_taxes else product_taxes
        )

        if not taxes:
            return base_price

        currency = website.currency_id
        totals = taxes.compute_all(
            base_price,
            currency=currency,
            quantity=1.0,
            product=delivery_product,
            partner=partner,
        )
        return float(totals.get("total_included", base_price) or 0.0)
    except Exception as e:
        _logger.warning(
            "_get_delivery_product_display_price: Error computing delivery display price for product %s (id=%s): %s. Using list_price fallback.",
            delivery_product.name,
            delivery_product.id,
            str(e),
        )
        return float(delivery_product.list_price or 0.0)
