# app/modules/dev_tools/routes/api/admin_platform_debug.py
"""
Platform resolution debug endpoint.

Simulates the middleware pipeline for arbitrary host/path combos
to diagnose platform context issues across all URL patterns.
"""

import logging

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.api.deps import get_current_super_admin_api, get_db
from app.modules.tenancy.schemas.auth import UserContext

router = APIRouter(prefix="/debug")
logger = logging.getLogger(__name__)


class PlatformTraceStep(BaseModel):
    step: str
    result: dict | None = None
    note: str = ""


class PlatformTraceResponse(BaseModel):
    input_host: str
    input_path: str
    input_platform_code_body: str | None = None
    steps: list[PlatformTraceStep]
    resolved_platform_code: str | None = None
    resolved_platform_id: int | None = None
    resolved_store_code: str | None = None
    resolved_store_id: int | None = None
    login_platform_source: str | None = None
    login_platform_code: str | None = None


@router.get("/platform-trace", response_model=PlatformTraceResponse)
def trace_platform_resolution(
    host: str = Query(..., description="Host header to simulate (e.g. localhost:8000, www.omsflow.lu)"),
    path: str = Query(..., description="URL path to simulate (e.g. /platforms/loyalty/store/WIZATECH/login)"),
    platform_code_body: str | None = Query(None, description="platform_code sent in login request body (Source 2)"),
    store_code_body: str | None = Query(None, description="store_code sent in login request body"),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_super_admin_api),
):
    """
    Simulate the full middleware + login platform resolution pipeline.

    Traces each step:
    1. PlatformContextMiddleware detection
    2. Platform DB lookup
    3. Path rewrite
    4. StoreContextMiddleware detection
    5. Store DB lookup
    6. Login handler Source 1/2/3 resolution
    """
    from middleware.platform_context import (
        _LOCAL_HOSTS,
        MAIN_PLATFORM_CODE,
        PlatformContextMiddleware,
    )
    from middleware.store_context import StoreContextManager

    steps = []
    mw = PlatformContextMiddleware(None)

    # ── Step 1: Platform detection ──
    platform_context = mw._detect_platform_context(path, host)
    steps.append(PlatformTraceStep(
        step="1. PlatformContextMiddleware._detect_platform_context()",
        result=_sanitize(platform_context),
        note="Returns None for admin routes or /store/ on localhost without /platforms/ prefix",
    ))

    # ── Step 1b: Referer fallback (storefront API on localhost) ──
    from middleware.platform_context import _strip_port
    host_without_port = _strip_port(host)
    if (
        host_without_port in _LOCAL_HOSTS
        and path.startswith("/api/v1/storefront/")
        and platform_context
        and platform_context.get("detection_method") == "default"
    ):
        steps.append(PlatformTraceStep(
            step="1b. Referer fallback check",
            note="Would check Referer header for /platforms/{code}/ — not simulated here",
        ))

    # ── Step 2: Platform DB lookup ──
    from middleware.platform_context import PlatformContextManager

    platform = None
    if platform_context:
        platform = PlatformContextManager.get_platform_from_context(db, platform_context)

    platform_dict = None
    if platform:
        platform_dict = {
            "id": platform.id,
            "code": platform.code,
            "name": platform.name,
            "domain": platform.domain,
            "path_prefix": platform.path_prefix,
        }
    steps.append(PlatformTraceStep(
        step="2. PlatformContextManager.get_platform_from_context()",
        result=platform_dict,
        note="DB lookup using detection context" if platform_context else "Skipped — no platform_context",
    ))

    # ── Step 3: Path rewrite ──
    clean_path = path
    if platform_context and platform_context.get("detection_method") == "path":
        clean_path = platform_context.get("clean_path", path)

    steps.append(PlatformTraceStep(
        step="3. Path rewrite (scope['path'])",
        result={"original_path": path, "rewritten_path": clean_path},
        note="Dev mode strips /platforms/{code}/ prefix for routing"
        if clean_path != path
        else "No rewrite — path unchanged",
    ))

    # ── Step 4: StoreContextMiddleware detection ──
    # Simulate what StoreContextManager.detect_store_context() would see
    # It reads request.state.platform_clean_path which is set to clean_path
    store_context = StoreContextManager._detect_store_from_host_and_path(host, clean_path, platform)
    if store_context and platform:
        store_context["_platform"] = platform

    steps.append(PlatformTraceStep(
        step="4. StoreContextManager.detect_store_context()",
        result=_sanitize(store_context),
        note="Uses rewritten path (platform_clean_path) for detection",
    ))

    # ── Step 5: Store DB lookup ──
    store = None
    if store_context:
        store = StoreContextManager.get_store_from_context(db, store_context)

    store_dict = None
    if store:
        store_dict = {
            "id": store.id,
            "store_code": store.store_code,
            "subdomain": store.subdomain,
            "name": store.name,
        }
    steps.append(PlatformTraceStep(
        step="5. StoreContextManager.get_store_from_context()",
        result=store_dict,
        note="DB lookup using store context" if store_context else "Skipped — no store_context",
    ))

    # ── Step 6: Is this an API path? ──
    # The actual API path after rewrite determines middleware behavior
    is_api = clean_path.startswith("/api/")
    is_store_api = clean_path.startswith(("/store/", "/api/v1/store/"))
    steps.append(PlatformTraceStep(
        step="6. Route classification",
        result={
            "is_api_path": is_api,
            "is_store_path": is_store_api,
            "clean_path": clean_path,
            "middleware_skips_store_detection_for_api": is_api and not clean_path.startswith("/api/v1/storefront/"),
        },
        note="StoreContextMiddleware SKIPS store detection for non-storefront API routes",
    ))

    # ── Step 7: Login handler — Source 1/2/3 ──
    from app.modules.tenancy.services.platform_service import platform_service

    login_source = None
    login_platform = None
    login_platform_code = None

    # Source 1: middleware platform (only if not "main")
    src1_note = ""
    if platform and platform.code != MAIN_PLATFORM_CODE:
        login_platform = platform
        login_source = "Source 1: middleware (request.state.platform)"
        src1_note = f"platform.code={platform.code!r} != 'main' → used"
    elif platform:
        src1_note = f"platform.code={platform.code!r} == 'main' → skipped"
    else:
        src1_note = "No platform in middleware → skipped"

    steps.append(PlatformTraceStep(
        step="7a. Login Source 1: middleware platform",
        result={"platform_code": platform.code if platform else None, "used": login_source is not None},
        note=src1_note,
    ))

    # Source 2: platform_code from body
    src2_note = ""
    if login_platform is None and platform_code_body:
        body_platform = platform_service.get_platform_by_code_optional(db, platform_code_body)
        if body_platform:
            login_platform = body_platform
            login_source = "Source 2: request body (platform_code)"
            src2_note = f"Found platform with code={platform_code_body!r} → used"
        else:
            src2_note = f"No platform found for code={platform_code_body!r} → would raise error"
    elif login_platform is not None:
        src2_note = "Skipped — Source 1 already resolved"
    else:
        src2_note = "No platform_code in body → skipped"

    steps.append(PlatformTraceStep(
        step="7b. Login Source 2: request body platform_code",
        result={"platform_code_body": platform_code_body, "used": login_source == "Source 2: request body (platform_code)"},
        note=src2_note,
    ))

    # Source 3: fallback to store's first active platform
    src3_note = ""
    if login_platform is None and store:
        primary_pid = platform_service.get_first_active_platform_id_for_store(db, store.id)
        if primary_pid:
            login_platform = platform_service.get_platform_by_id(db, primary_pid)
            login_source = "Source 3: get_first_active_platform_id_for_store()"
            src3_note = f"Fallback → first active platform for store {store.store_code}: platform_id={primary_pid}"
        else:
            src3_note = "No active platform for store"
    elif login_platform is not None:
        src3_note = "Skipped — earlier source already resolved"
        # Still show what Source 3 WOULD return for comparison
        if store:
            primary_pid = platform_service.get_first_active_platform_id_for_store(db, store.id)
            if primary_pid:
                fallback = platform_service.get_platform_by_id(db, primary_pid)
                src3_note += f" (would have been: {fallback.code!r})"
    else:
        src3_note = "No store resolved — cannot determine fallback"

    steps.append(PlatformTraceStep(
        step="7c. Login Source 3: store's first active platform (fallback)",
        result={
            "store_id": store.id if store else None,
            "used": login_source == "Source 3: get_first_active_platform_id_for_store()",
        },
        note=src3_note,
    ))

    if login_platform:
        login_platform_code = login_platform.code

    # ── Step 8: Final result ──
    steps.append(PlatformTraceStep(
        step="8. FINAL LOGIN PLATFORM",
        result={
            "source": login_source,
            "platform_code": login_platform_code,
            "platform_id": login_platform.id if login_platform else None,
        },
        note=f"JWT will be minted with platform_code={login_platform_code!r}",
    ))

    return PlatformTraceResponse(
        input_host=host,
        input_path=path,
        input_platform_code_body=platform_code_body,
        steps=steps,
        resolved_platform_code=platform.code if platform else None,
        resolved_platform_id=platform.id if platform else None,
        resolved_store_code=store.store_code if store else None,
        resolved_store_id=store.id if store else None,
        login_platform_source=login_source,
        login_platform_code=login_platform_code,
    )



# ── Isolation Audit models ──


class IsolationFinding(BaseModel):
    check: str
    check_label: str
    risk: str
    resolved_value: str | None = None
    source: str
    source_label: str
    is_explicit: bool
    note: str = ""


class StoreIsolationResult(BaseModel):
    store_code: str
    store_name: str
    merchant_name: str | None = None
    finding_counts: dict[str, int]
    findings: list[IsolationFinding]


class IsolationAuditResponse(BaseModel):
    total_stores: int
    stores_with_findings: int
    summary: dict[str, int]
    results: list[StoreIsolationResult]


@router.get("/isolation-audit", response_model=IsolationAuditResponse)
def isolation_audit(
    store_code: str | None = Query(None, description="Filter to a single store by store_code"),
    risk: str | None = Query(None, description="Filter by risk level: critical, high, medium"),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_super_admin_api),
):
    """
    Audit tenant isolation across all stores.

    Scans every active store and reports where configuration values come from,
    flagging silent inheritance, missing data, and potential data commingling.
    """
    from app.modules.dev_tools.services.isolation_audit_service import (
        run_isolation_audit,
    )

    return run_isolation_audit(db, store_code=store_code, risk_filter=risk)


class DomainHealthEntry(BaseModel):
    domain: str
    type: str  # "subdomain" or "custom_domain"
    platform_code: str | None = None
    expected_store: str | None = None
    resolved_store: str | None = None
    status: str  # "pass" or "fail"
    note: str = ""


class DomainHealthResponse(BaseModel):
    total: int
    passed: int
    failed: int
    details: list[DomainHealthEntry]


@router.get("/domain-health", response_model=DomainHealthResponse)
def domain_health_check(
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_super_admin_api),
):
    """
    Simulate the middleware resolution pipeline for every active
    StorePlatform custom subdomain and StoreDomain custom domain.

    Returns a pass/fail report showing whether each domain resolves
    to the expected store.
    """
    from app.modules.dev_tools.services.domain_health_service import (
        run_domain_health_check,
    )

    return run_domain_health_check(db)


def _sanitize(d: dict | None) -> dict | None:
    """Remove non-serializable objects from dict."""
    if d is None:
        return None
    return {k: v for k, v in d.items() if not k.startswith("_")}
