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

from odoo import fields, http
from odoo.http import request
from odoo.tools.translate import _

from odoo.addons.website_membership.controllers.main import (
    WebsiteMembership as BaseWebsiteMembership,
)


class WebsiteMembership(BaseWebsiteMembership):
    @http.route()
    def members(
        self, membership_id=None, country_name=None, country_id=0, page=1, **post
    ):
        request.env = request.env(
            context=dict(request.env.context, include_associate_members=True)
        )
        response = super().members(
            membership_id=membership_id,
            country_name=country_name,
            country_id=country_id,
            page=page,
            **post,
        )
        self._add_associate_members(response, membership_id, country_id, page, post)
        return response

    def _add_associate_members(
        self, response, membership_id, country_id, page, post
    ):
        values = response.qcontext
        memberships_partner_ids = values.get("memberships_partner_ids", {})
        if not memberships_partner_ids:
            return

        post_name = post.get("search") or post.get("name", "")
        host_keys = {}
        for key, ids in memberships_partner_ids.items():
            for partner_id in ids:
                host_keys.setdefault(partner_id, []).append(key)
        host_ids = set(host_keys.keys())
        hosts = {
            pid: partner
            for pid, partner in values.get("partners", {}).items()
            if pid in host_ids
        }

        associates = (
            request.env["membership.membership_line"]
            .sudo()
            ._get_associate_partners(
                tuple(host_ids), country_id=country_id, post_name=post_name
            )
        )

        new_memberships_partner_ids = self._filter_displayed_hosts(
            memberships_partner_ids, hosts, country_id, post_name
        )
        for associate in associates:
            keys = host_keys.get(associate.associate_member.id)
            if not keys:
                continue
            for key in keys:
                if associate.id not in new_memberships_partner_ids[key]:
                    new_memberships_partner_ids[key].append(associate.id)
        values["memberships_partner_ids"] = {
            key: ids
            for key, ids in new_memberships_partner_ids.items()
            if ids
        }

        displayed_ids = set(
            pid
            for ids in values["memberships_partner_ids"].values()
            for pid in ids
        )
        values["partners"] = {
            p.id: p
            for p in request.env["res.partner"]
            .sudo()
            .browse(list(displayed_ids))
        }

        all_host_ids, all_free_host_ids, all_associates = (
            self._get_all_visible_ids(membership_id, country_id, post_name)
        )
        self._recompute_countries(
            values, all_host_ids, all_free_host_ids, all_associates, country_id
        )
        self._update_google_map(values, country_id, post_name)
        self._update_pager(
            values,
            membership_id,
            country_id,
            page,
            post,
            all_host_ids,
            all_free_host_ids,
        )

    def _filter_displayed_hosts(
        self, memberships_partner_ids, hosts, country_id, post_name
    ):
        return {
            key: [
                pid
                for pid in ids
                if pid not in hosts
                or self._partner_matches_filters(hosts[pid], country_id, post_name)
            ]
            for key, ids in memberships_partner_ids.items()
        }

    def _partner_matches_filters(self, partner, country_id, post_name):
        if country_id and partner.country_id.id != country_id:
            return False
        if post_name and not self._partner_matches_search(partner, post_name):
            return False
        return True

    def _partner_matches_search(self, partner, post_name):
        term = post_name.lower()
        if term in (partner.name or "").lower():
            return True
        if term in (partner.website_description or "").lower():
            return True
        return False

    def _get_all_visible_ids(self, membership_id, country_id, post_name):
        free_selected = membership_id in (None, "free")
        if membership_id == "free":
            all_host_ids = set()
        else:
            all_host_ids = self._get_all_host_ids(
                membership_id, country_id, post_name
            )
        all_free_host_ids = (
            self._get_all_free_host_ids(country_id, post_name)
            if free_selected
            else set()
        )
        all_associates = (
            request.env["membership.membership_line"]
            .sudo()
            ._get_associate_partners(
                tuple(all_host_ids | all_free_host_ids),
                country_id=country_id,
                post_name=post_name,
            )
        )
        return all_host_ids, all_free_host_ids, all_associates

    def _get_all_host_ids(self, membership_id, country_id, post_name):
        Product = request.env["product.product"]
        MembershipLine = request.env["membership.membership_line"]
        today = fields.Date.today()
        products = Product.sudo().search([("membership", "=", True)])
        domain = [
            ("partner.website_published", "=", True),
            ("state", "=", "paid"),
            ("date_to", ">=", today),
            ("date_from", "<=", today),
            ("membership_id", "in", products.ids),
        ]
        if membership_id and membership_id != "free":
            domain.append(("membership_id", "=", int(membership_id)))
        if post_name:
            domain += [
                "|",
                ("partner.name", "ilike", post_name),
                ("partner.website_description", "ilike", post_name),
            ]
        if country_id:
            domain.append(("partner.country_id", "=", country_id))
        lines = MembershipLine.sudo().search(domain)
        return set(lines.partner.ids)

    def _get_all_free_host_ids(self, country_id, post_name):
        domain = [
            ("membership_state", "=", "free"),
            ("website_published", "=", True),
        ]
        if post_name:
            domain += [
                "|",
                ("name", "ilike", post_name),
                ("website_description", "ilike", post_name),
            ]
        if country_id:
            domain.append(("country_id", "=", country_id))
        return set(request.env["res.partner"].sudo().search(domain).ids)

    def _recompute_countries(
        self, values, all_host_ids, all_free_host_ids, all_associates, country_id
    ):
        visible_ids = all_host_ids | all_free_host_ids | set(all_associates.ids)
        values["countries"] = self._build_countries(visible_ids, country_id)

    def _build_countries(self, visible_ids, country_id):
        Partner = request.env["res.partner"]
        Country = request.env["res.country"]
        current_country = None
        countries = Partner.sudo().read_group(
            [("id", "in", list(visible_ids))],
            ["__count"],
            groupby="country_id",
        )
        countries_total = sum(
            country_dict["country_id_count"] for country_dict in countries
        )
        if country_id:
            current_country = Country.browse(country_id).read(["id", "name"])[0]
            if not any(
                x["country_id"] and x["country_id"][0] == country_id
                for x in countries
            ):
                countries.append(
                    {
                        "country_id_count": 0,
                        "country_id": (country_id, current_country["name"]),
                    }
                )
                countries = [d for d in countries if d["country_id"]]
                countries.sort(key=lambda d: d["country_id"][1])
        countries.insert(
            0,
            {
                "country_id_count": countries_total,
                "country_id": (0, _("All Countries")),
            },
        )
        return countries

    def _update_google_map(self, values, country_id, post_name):
        map_ids_str = values.get("google_map_partner_ids", "")
        if not map_ids_str:
            return
        map_ids = [int(pid) for pid in map_ids_str.split(",") if pid]
        if not map_ids:
            return
        partners = request.env["res.partner"].sudo().browse(map_ids)
        if country_id:
            partners = partners.filtered(
                lambda p: p.country_id.id == country_id
            )
        if post_name:
            partners = partners.filtered(
                lambda p: self._partner_matches_search(p, post_name)
            )
        values["google_map_partner_ids"] = ",".join(
            str(pid) for pid in partners.ids
        )

    def _update_pager(
        self,
        values,
        membership_id,
        country_id,
        page,
        post,
        all_host_ids,
        all_free_host_ids,
    ):
        post_name = post.get("search") or post.get("name", "")
        if membership_id == "free":
            displayed_host_ids = set()
        else:
            host_records = request.env["res.partner"].sudo().browse(
                list(all_host_ids)
            )
            displayed_host_ids = {
                host.id
                for host in host_records
                if self._partner_matches_filters(host, country_id, post_name)
            }
        count_members = len(displayed_host_ids) + len(all_free_host_ids)
        limit = self._references_per_page
        base_url = "/members%s%s" % (
            "/association/%s" % membership_id if membership_id else "",
            "/country/%s" % country_id if country_id else "",
        )
        values["pager"] = request.website.pager(
            url=base_url,
            total=count_members,
            page=page,
            step=limit,
            scope=7,
            url_args=post,
        )
        values["search_count"] = count_members
