"""Rule-based recommendation + assessment engines (no AI)."""
from __future__ import annotations

from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.commercial.repository import CommercialRegistryRepository
from app.commercial.service import CommercialRegistryService


def _match_rule(match: dict[str, Any], req: dict[str, Any]) -> bool:
    if not match:
        return True
    for key, expected in match.items():
        if key.endswith("_gte"):
            field = key[:-4]
            actual = req.get(field)
            try:
                if actual is None or float(actual) < float(expected):
                    return False
            except (TypeError, ValueError):
                return False
        elif key.endswith("_in"):
            field = key[:-3]
            actual = req.get(field)
            if actual not in (expected or []):
                return False
        elif key == "needs_contains":
            needs = req.get("needs") or []
            if expected not in needs:
                return False
        elif key.startswith("flag_"):
            field = key[5:]
            if bool(req.get(field)) is not bool(expected):
                return False
        else:
            if req.get(key) != expected:
                # also support {eq: x} objects
                if isinstance(expected, dict) and "eq" in expected:
                    if req.get(key) != expected["eq"]:
                        return False
                elif isinstance(expected, dict) and "in" in expected:
                    if req.get(key) not in expected["in"]:
                        return False
                elif isinstance(expected, dict) and "gte" in expected:
                    try:
                        if float(req.get(key) or 0) < float(expected["gte"]):
                            return False
                    except (TypeError, ValueError):
                        return False
                else:
                    return False
    return True


def _rank_add(
    bucket: dict[str, dict[str, Any]], code: str, score: float, reason: str
) -> None:
    if not code:
        return
    cur = bucket.get(code)
    if cur is None or score > cur["score"]:
        bucket[code] = {"code": code, "score": score, "reason": reason}


class RecommendationEngine:
    def __init__(self, db: AsyncSession) -> None:
        self.db = db
        self.repo = CommercialRegistryRepository(db)
        self.registry = CommercialRegistryService(db)

    async def evaluate(self, request: dict[str, Any]) -> dict[str, Any]:
        rules_rows, _ = await self.repo.list_objects(
            "recommendation_rules",
            status="published",
            platform_only=True,
            limit=500,
        )
        # Normalize request aliases from FE assessment answers
        req = dict(request)
        if "business_type_code" not in req and request.get("answers"):
            answers = request["answers"]
            if isinstance(answers, dict):
                req.update({k: v for k, v in answers.items() if not isinstance(v, (dict, list)) or True})
                for k, v in answers.items():
                    req[k] = v

        products: dict[str, dict[str, Any]] = {}
        bundles: dict[str, dict[str, Any]] = {}
        capabilities: dict[str, dict[str, Any]] = {}
        pricing: dict[str, dict[str, Any]] = {}
        plans: dict[str, dict[str, Any]] = {}
        services: dict[str, dict[str, Any]] = {}
        automation: dict[str, dict[str, Any]] = {}
        assets: dict[str, dict[str, Any]] = {}
        rule_trace: list[str] = []

        # Sort by priority desc
        def priority(row) -> int:
            return int((row.payload or {}).get("priority") or 0)

        for row in sorted(rules_rows, key=priority, reverse=True):
            payload = dict(row.payload or {})
            if payload.get("active") is False:
                continue
            match = payload.get("match") or {}
            if not _match_rule(match, req):
                continue
            emit = payload.get("emit") or {}
            reason = emit.get("reason") or payload.get("reason") or row.display_name
            score = float(emit.get("score") or 50)
            rule_trace.append(f"{row.stable_code}: matched → {reason}")

            for code in emit.get("products") or emit.get("recommended_products") or []:
                _rank_add(products, str(code), score, reason)
            for code in emit.get("bundles") or emit.get("recommended_bundles") or []:
                _rank_add(bundles, str(code), score, reason)
            for code in emit.get("capabilities") or []:
                _rank_add(capabilities, str(code), score, reason)
            for code in emit.get("pricing") or emit.get("recommended_pricing") or []:
                _rank_add(pricing, str(code), score, reason)
            for code in emit.get("plans") or emit.get("recommended_plans") or []:
                _rank_add(plans, str(code), score, reason)
            if emit.get("plan_code"):
                _rank_add(plans, str(emit["plan_code"]), score, reason)
            for code in emit.get("services") or []:
                _rank_add(services, str(code), score, reason)
            for code in emit.get("automation_packs") or []:
                _rank_add(automation, str(code), score, reason)
            for code in emit.get("assets") or []:
                _rank_add(assets, str(code), score, reason)

            # single-code shorthand
            if emit.get("bundle_code"):
                _rank_add(bundles, str(emit["bundle_code"]), score, reason)
            if emit.get("product_code"):
                _rank_add(products, str(emit["product_code"]), score, reason)

            if payload.get("stop"):
                rule_trace.append(f"{row.stable_code}: stop")
                break

        # Heuristic fallback if no rules published yet
        if not rule_trace:
            bt = str(req.get("business_type_code") or req.get("business_type") or "")
            rule_trace.append("fallback: no published recommendation_rules; heuristic")
            if bt:
                bundles_rows, _ = await self.repo.list_objects(
                    "bundles", status="published", platform_only=True, limit=200
                )
                for b in bundles_rows:
                    types = (b.payload or {}).get("business_types") or []
                    if bt in types or not types:
                        _rank_add(bundles, b.stable_code, 60 if bt in types else 20, f"business_type={bt}")
                products_rows, _ = await self.repo.list_objects(
                    "products", status="published", platform_only=True, limit=200
                )
                for p in products_rows:
                    industries = (p.payload or {}).get("recommended_industries") or []
                    if bt in industries or not industries:
                        _rank_add(products, p.stable_code, 55 if bt in industries else 15, f"industry/type={bt}")

        def ranked(bucket: dict[str, dict[str, Any]]) -> list[dict[str, Any]]:
            return sorted(bucket.values(), key=lambda x: x["score"], reverse=True)

        return {
            "version": "recommendation.v1",
            "recommended_products": ranked(products),
            "recommended_bundles": ranked(bundles),
            "recommended_capabilities": ranked(capabilities),
            "recommended_pricing": ranked(pricing),
            "recommended_plans": ranked(plans),
            "recommended_services": ranked(services),
            "recommended_automation_packs": ranked(automation),
            "recommended_assets": ranked(assets),
            "rule_trace": rule_trace,
        }


class AssessmentEngine:
    def __init__(self, db: AsyncSession) -> None:
        self.db = db
        self.repo = CommercialRegistryRepository(db)
        self.registry = CommercialRegistryService(db)

    async def get_schema(self, schema_code: str | None = None) -> dict[str, Any]:
        if schema_code:
            row = await self.repo.get_by_code("assessment_schemas", schema_code)
        else:
            rows, _ = await self.repo.list_objects(
                "assessment_schemas", status="published", platform_only=True, limit=20
            )
            row = rows[0] if rows else None
        if row is None:
            # Default configurable schema (still stored shape; not FE-hardcoded questions forever —
            # returned only when registry empty so admin can replace via API)
            return {
                "schema_code": "default.business.assessment",
                "title": "ارزیابی کسب‌وکار",
                "version": "1",
                "questions": [
                    {
                        "question_id": "business_type_code",
                        "type": "select",
                        "label": "نوع کسب‌وکار",
                        "required": True,
                        "options_source": "business_types",
                    },
                    {
                        "question_id": "company_size",
                        "type": "select",
                        "label": "اندازه سازمان",
                        "required": True,
                        "options": [
                            {"value": "solo", "label": "تک‌نفره"},
                            {"value": "small", "label": "کوچک"},
                            {"value": "medium", "label": "متوسط"},
                            {"value": "large", "label": "بزرگ"},
                            {"value": "enterprise", "label": "سازمانی"},
                        ],
                    },
                    {
                        "question_id": "goals",
                        "type": "multi_select",
                        "label": "اهداف",
                        "required": False,
                        "options": [
                            {"value": "online_sales", "label": "فروش آنلاین"},
                            {"value": "delivery", "label": "پیک"},
                            {"value": "crm", "label": "CRM"},
                            {"value": "loyalty", "label": "وفاداری"},
                            {"value": "website", "label": "وب‌سایت"},
                        ],
                    },
                ],
                "source": "builtin_default_until_registry_seeded",
            }
        payload = dict(row.payload or {})
        return {
            "schema_code": row.stable_code,
            "title": row.display_name,
            "version": row.version_code,
            "questions": payload.get("questions") or [],
            "metadata": row.meta_data or {},
            "source": "registry",
        }
