# app/modules/dev_tools/services/domain_health_service.py
"""
Domain Health Check Service

Simulates the middleware resolution pipeline for every active access method
(custom subdomain, default subdomain, custom domain, path-based) to verify
they resolve to the expected store.
"""

import logging

from sqlalchemy.orm import Session

logger = logging.getLogger(__name__)


def run_domain_health_check(db: Session) -> dict:
    """
    Check all active store access methods by simulating middleware resolution:
    1. StorePlatform custom subdomains  (acme-rewards.rewardflow.lu)
    2. Default subdomains per platform  (acme.omsflow.lu)
    3. StoreDomain custom domains       (wizatech.shop)
    4. Path-based store routes          (/storefront/{subdomain}/, /store/{subdomain}/)

    Returns:
        dict with keys: total, passed, failed, details (list of entry dicts)
    """
    from app.modules.tenancy.models import Platform, Store, StoreDomain
    from app.modules.tenancy.models.store_platform import StorePlatform
    from middleware.store_context import StoreContextManager

    details: list[dict] = []

    # Pre-fetch all active stores, platforms, and memberships
    active_stores = (
        db.query(Store).filter(Store.is_active.is_(True)).all()  # noqa: SVC-005
    )
    store_by_id: dict[int, Store] = {s.id: s for s in active_stores}

    all_platforms = (
        db.query(Platform).filter(Platform.is_active.is_(True)).all()
    )
    platform_by_id: dict[int, Platform] = {p.id: p for p in all_platforms}

    all_store_platforms = (
        db.query(StorePlatform)
        .filter(StorePlatform.is_active.is_(True))
        .all()
    )

    # Build store_id → list of (StorePlatform, Platform) for reuse
    store_memberships: dict[int, list[tuple]] = {}
    for sp in all_store_platforms:
        platform = platform_by_id.get(sp.platform_id)
        if platform:
            store_memberships.setdefault(sp.store_id, []).append((sp, platform))

    # ── 1. Custom subdomains (StorePlatform.custom_subdomain) ──
    # e.g. acme-rewards.rewardflow.lu
    for sp in all_store_platforms:
        if not sp.custom_subdomain:
            continue
        expected_store = store_by_id.get(sp.store_id)
        platform = platform_by_id.get(sp.platform_id)
        if not expected_store or not platform:
            continue

        context = {
            "detection_method": "subdomain",
            "subdomain": sp.custom_subdomain,
            "_platform": platform,
        }
        resolved = StoreContextManager.get_store_from_context(db, context)

        passed = resolved is not None and resolved.id == expected_store.id
        details.append(_entry(
            domain=f"{sp.custom_subdomain}.{platform.domain or platform.code}",
            entry_type="custom subdomain",
            platform_code=platform.code,
            expected_store=expected_store,
            resolved_store=resolved,
            passed=passed,
            note="" if passed else "custom_subdomain lookup failed",
        ))

    # ── 2. Default subdomains per platform ──
    # e.g. acme.omsflow.lu — uses Store.subdomain on the platform domain.
    # With fail-closed routing, this only works if StorePlatform.custom_subdomain
    # matches Store.subdomain, so this check surfaces missing entries.
    for store in active_stores:
        if not store.subdomain:
            continue
        memberships = store_memberships.get(store.id, [])
        for sp, platform in memberships:
            if not platform.domain:
                continue
            # If custom_subdomain is already set to this value, it's covered
            # in check #1 above — but we still check default subdomain access
            # to confirm it resolves (it exercises the same code path).
            # Skip if custom_subdomain == subdomain to avoid duplicate entries.
            if sp.custom_subdomain and sp.custom_subdomain.lower() == store.subdomain.lower():
                continue

            context = {
                "detection_method": "subdomain",
                "subdomain": store.subdomain,
                "_platform": platform,
            }
            resolved = StoreContextManager.get_store_from_context(db, context)

            passed = resolved is not None and resolved.id == store.id
            note = ""
            if not passed:
                note = (
                    f"Store '{store.subdomain}' not found or has no active "
                    f"membership on platform {platform.code}"
                )
            details.append(_entry(
                domain=f"{store.subdomain}.{platform.domain}",
                entry_type="default subdomain",
                platform_code=platform.code,
                expected_store=store,
                resolved_store=resolved,
                passed=passed,
                note=note,
            ))

    # ── 3. Custom domains (StoreDomain) ──
    # e.g. wizatech.shop
    store_domains = (
        db.query(StoreDomain)
        .filter(
            StoreDomain.is_active.is_(True),
            StoreDomain.is_verified.is_(True),
        )
        .all()
    )
    for sd in store_domains:
        expected_store = sd.store
        if not expected_store:
            continue

        platform = sd.platform
        platform_code = platform.code if platform else None

        context = {
            "detection_method": "custom_domain",
            "domain": sd.domain,
        }
        resolved = StoreContextManager.get_store_from_context(db, context)

        passed = resolved is not None and resolved.id == expected_store.id
        details.append(_entry(
            domain=sd.domain,
            entry_type="custom domain",
            platform_code=platform_code,
            expected_store=expected_store,
            resolved_store=resolved,
            passed=passed,
            note="" if passed else "custom domain resolution failed",
        ))

    # ── 4. Path-based routes ──
    # e.g. /storefront/luxweb/, /store/luxweb/
    # Path-based URLs use Store.subdomain as the path segment.
    for store in active_stores:
        if not store.subdomain:
            continue

        memberships = store_memberships.get(store.id, [])
        platform_codes = sorted(p.code for _, p in memberships) if memberships else []
        platform_label = ", ".join(platform_codes) if platform_codes else None

        for prefix, label in [
            ("/storefront/", "storefront"),
            ("/store/", "store"),
        ]:
            context = {
                "detection_method": "path",
                "subdomain": store.subdomain,
                "path_prefix": f"{prefix}{store.subdomain}",
                "full_prefix": prefix,
            }
            resolved = StoreContextManager.get_store_from_context(db, context)

            passed = resolved is not None and resolved.id == store.id
            details.append(_entry(
                domain=f"{prefix}{store.subdomain}/",
                entry_type=f"path ({label})",
                platform_code=platform_label,
                expected_store=store,
                resolved_store=resolved,
                passed=passed,
                note="" if passed else f"path-based {label} resolution failed",
            ))

    total = len(details)
    passed_count = sum(1 for d in details if d["status"] == "pass")

    return {
        "total": total,
        "passed": passed_count,
        "failed": total - passed_count,
        "details": details,
    }


def _entry(
    domain: str,
    entry_type: str,
    platform_code: str | None,
    expected_store,
    resolved_store,
    passed: bool,
    note: str,
) -> dict:
    """Build a single health-check result entry."""
    return {
        "domain": domain,
        "type": entry_type,
        "platform_code": platform_code,
        "expected_store": (
            getattr(expected_store, "store_code", expected_store.subdomain)
            if expected_store else None
        ),
        "resolved_store": (
            getattr(resolved_store, "store_code", resolved_store.subdomain)
            if resolved_store else None
        ),
        "status": "pass" if passed else "fail",
        "note": note,
    }
