# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models


class PosOrderReport(models.Model):
    _inherit = "report.pos.order"

    # --- Time dimensions ----------------------------------------------------
    date_day = fields.Date(
        string="Order Day",
        readonly=True,
    )
    hour_of_day = fields.Integer(
        string="Hour of Day",
        readonly=True,
        group_operator=None,
    )
    day_of_week = fields.Integer(
        string="Day of Week (1=Mon, 7=Sun)",
        readonly=True,
        group_operator=None,
    )

    # --- Category hierarchy -------------------------------------------------
    # Family = root category, subfamily = direct parent of categ_id.
    # Computed from product.category.parent_path (materialized path
    # "1/3/7/"), which is maintained automatically by Odoo (_parent_store).
    categ_root_id = fields.Many2one(
        "product.category",
        string="Product Family (root)",
        readonly=True,
    )
    categ_root_id_int = fields.Integer(
        string="Product Family ID (root)",
        readonly=True,
        group_operator=None,
    )
    categ_parent_id = fields.Many2one(
        "product.category",
        string="Product Subfamily (parent)",
        readonly=True,
    )
    categ_parent_id_int = fields.Integer(
        string="Product Subfamily ID (parent)",
        readonly=True,
        group_operator=None,
    )
    product_categ_id_int = fields.Integer(
        string="Product Category ID (leaf)",
        readonly=True,
        group_operator=None,
    )
    product_id_int = fields.Integer(
        string="Product ID",
        readonly=True,
        group_operator=None,
    )

    # --- Cooperative member dimension --------------------------------------
    # True when the customer of the ticket held at least one share line whose
    # effective_date was on or before the order date, in the same company.
    #
    # NOTE: this is an approximation. cooperator does not store an end date
    # on share.line; share buy-backs and transfers are recorded only in
    # subscription.register. A future version may refine the calculation by
    # consuming subscription.register to substract sell_back/transfer
    # operations, at the cost of a more expensive subquery.
    is_member_at_date = fields.Boolean(
        string="Member at Order Date",
        readonly=True,
    )

    def _select(self):
        return (
            super()._select()
            + """,
            s.date_order::date AS date_day,
            EXTRACT(HOUR FROM (s.date_order AT TIME ZONE 'UTC') AT TIME ZONE 'Europe/Madrid')::int AS hour_of_day,
            EXTRACT(ISODOW FROM s.date_order)::int AS day_of_week,
            cat_root.id AS categ_root_id,
            cat_root.id AS categ_root_id_int,
            pt_categ.parent_id AS categ_parent_id,
            pt_categ.parent_id AS categ_parent_id_int,
            pt.categ_id AS product_categ_id_int,
            pt.id AS product_id_int,
            CASE WHEN EXISTS (
                SELECT 1 FROM share_line sl
                WHERE sl.partner_id = s.partner_id
                  AND sl.company_id = s.company_id
                  AND sl.effective_date IS NOT NULL
                  AND sl.effective_date <= s.date_order::date
            ) THEN TRUE ELSE FALSE END AS is_member_at_date
            """
        )

    def _from(self):
        # Extra joins to resolve:
        #  - pt_categ: direct category of the product (used for parent_id and
        #              parent_path lookup).
        #  - cat_root: root category, found by taking the first id in
        #              parent_path. parent_path looks like "1/3/7/".
        return (
            super()._from()
            + """
            LEFT JOIN product_category pt_categ ON pt_categ.id = pt.categ_id
            LEFT JOIN product_category cat_root
                ON cat_root.id = NULLIF(
                    split_part(pt_categ.parent_path, '/', 1), ''
                )::int
            """
        )

    def _group_by(self):
        # Append non-aggregated expressions added in _select() that are not
        # already in the parent GROUP BY. s.date_order is already there
        # (point_of_sale.report.pos_order_report._group_by), so the time
        # dimensions derived from it (date_day, hour_of_day, day_of_week)
        # are functionally dependent and don't need to be listed.
        # is_member_at_date is a scalar subquery; not needed either.
        return (
            super()._group_by()
            + """,
            cat_root.id,
            pt_categ.parent_id,
            pt.categ_id,
            pt.id
            """
        )
