"""Deterministic analysis and planning for imported application source."""

from __future__ import annotations

import re
from collections.abc import Iterable
from pathlib import Path
from typing import Any, Literal

SourceAdoptionMode = Literal["add", "merge", "inject", "replace", "migrate"]
DeploymentIntent = Literal["modify_only", "modify_and_deploy"]

_P2HS_OWNED = {
    "auth": ("auth", "login", "logout", "session", "supabase auth", "firebase auth"),
    "records": ("supabase", "firebase firestore", "prisma", "drizzle", "database", "crud"),
    "files": ("upload", "s3", "r2", "storage", "file upload"),
    "email": ("sendgrid", "resend", "smtp", "send email", "email.send"),
    "payments": ("stripe", "paypal", "checkout", "payment"),
    "messaging": ("slack", "teams", "send message", "messaging"),
}
_KNOWN_ADAPTERS = {"stripe", "paypal", "sendgrid", "resend", "twilio", "hubspot", "salesforce"}
_P2HS_TARGETS = {
    "auth": "auth",
    "records": "records",
    "files": "files.attachments",
    "email": "email.send",
    "payments": "payments.checkout.create",
    "messaging": "messaging.send",
}
_URL_RE = re.compile(r"https?://[^\s'\"<>)]{4,}", re.I)
_ROUTE_RE = re.compile(r"(?:href|to|path|route)\s*[:=]\s*['\"]([^'\"]+)['\"]", re.I)
_COMPONENT_RE = re.compile(
    r"(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)\b|<([A-Z][A-Za-z0-9_]*)\b"
)
_SECRET_RE = re.compile(
    r"(?P<prefix>(?:sk_live|sk_test|pk_live|pk_test|AIza|ghp_|github_pat_|xox[baprs]-)[A-Za-z0-9_\-]+)"
    r"|(?P<assignment>(?:api[_-]?key|token|secret|password|access[_-]?token)\s*[:=]\s*['\"])(?P<value>[^'\"\n]{8,})",
    re.I,
)
_TEXT_SUFFIXES = {
    ".css",
    ".html",
    ".js",
    ".jsx",
    ".json",
    ".md",
    ".mjs",
    ".ts",
    ".tsx",
    ".vue",
    ".yaml",
    ".yml",
}


# Files that carry a design's markup. A design cannot be preserved from data, scripts or
# styles alone: without one of these the build agent has no UI to adopt and can only
# recreate it from the prompt's description.
UI_MARKUP_SUFFIXES = (".html", ".htm", ".vue", ".svelte", ".tsx", ".jsx", ".astro")


def has_ui_markup(paths: Iterable[str]) -> bool:
    return any(str(p).lower().endswith(UI_MARKUP_SUFFIXES) for p in paths)


def missing_ui_source_message(paths: Iterable[str]) -> str:
    """Why a migrate request is refused, and exactly how to fix it (shown to the calling agent)."""
    names = [str(p) for p in paths]
    received = ", ".join(names[:8]) + (f" and {len(names) - 8} more" if len(names) > 8 else "")
    return (
        "source_adoption \"migrate\" converts an existing app, but the attachments contain no HTML, "
        "Vue, React or Svelte markup "
        f"(received: {received or 'no files'}). P2HS builds only from the files you attach and cannot "
        "open links such as a claude.ai artifact URL, so without the markup it would recreate the "
        "design from your description instead of keeping yours. Attach every file of the design "
        "(index.html, all CSS and JS, images; zip many files with request_asset_upload) and retry. "
        "If you want a new build from a description, omit source_adoption."
    )


def redact_source(text: str) -> str:
    """Remove credential values while retaining the integration shape."""

    def replace(match: re.Match[str]) -> str:
        if match.group("prefix"):
            return "<P2HS_SECRET_REDACTED>"
        return f"{match.group('assignment')}<P2HS_SECRET_REDACTED>"

    return _SECRET_RE.sub(replace, text)


def _unique(values: Iterable[str]) -> list[str]:
    return list(dict.fromkeys(v for v in values if v))


def analyze_imported_source(files: Iterable[dict[str, str]]) -> dict[str, Any]:
    """Return a stable ImportedSourceAnalysis-shaped dictionary."""
    rows = [{"path": str(f.get("path", "")), "content": str(f.get("content", ""))} for f in files]
    combined = "\n".join(f["content"] for f in rows)
    lowered = combined.lower()
    paths = [f["path"] for f in rows]
    framework = "unknown"
    if (
        any(p.lower().endswith(".vue") for p in paths)
        or "nuxt" in lowered
        or "nuxt.config" in lowered
    ):
        framework = "nuxt"
    elif (
        any(p.lower().endswith((".tsx", ".jsx")) for p in paths)
        or "react" in lowered
        or "from 'react'" in lowered
    ):
        framework = "react"
    elif any(p.lower().endswith(".svelte") for p in paths):
        framework = "svelte"
    elif any(p.lower().endswith(".vue") for p in paths):
        framework = "vue"
    elif any(p.lower().endswith((".html", ".htm")) for p in paths):
        framework = "html"

    urls = _unique(_URL_RE.findall(combined))
    hosts = _unique(
        [re.sub(r"^https?://", "", u, flags=re.I).split("/", 1)[0].lower() for u in urls]
    )
    integrations = []
    for name in sorted(_KNOWN_ADAPTERS):
        if name in lowered:
            integrations.append(
                {"name": name, "kind": "known_adapter", "status": "adapter_available"}
            )
    known_hosts = {"api.p2hs.com", "dashboard.p2hs.com", "cloud.p2hs.com", "mcp.p2hs.com"}
    for host in hosts:
        if host not in known_hosts and not any(
            host.endswith(x) for x in ("stripe.com", "paypal.com")
        ):
            integrations.append(
                {"name": host, "kind": "external", "status": "configuration_required"}
            )

    p2hs_ops = [op for op, terms in _P2HS_OWNED.items() if any(term in lowered for term in terms)]
    routes = _unique([r for r in _ROUTE_RE.findall(combined) if r.startswith("/")])
    components = _unique([a or b for a, b in _COMPONENT_RE.findall(combined)])[:200]
    secret_count = len(_SECRET_RE.findall(combined))
    env_deps = _unique(
        re.findall(r"(?:import\.meta\.env|process\.env)\.([A-Z][A-Z0-9_]+)", combined)
    )
    auth_assumptions = _unique(
        [
            "firebase" if "firebase" in lowered else "",
            "supabase" if "supabase" in lowered else "",
            "jwt" if "jwt" in lowered else "",
        ]
    )
    conflicts = []
    if "api.p2hs.com" in lowered or "cloud.p2hs.com" in lowered:
        conflicts.append("direct_p2hs_cloud_or_private_endpoint")
    if any(x in lowered for x in ("firebase auth", "supabase auth", "own user table")):
        conflicts.append("parallel_auth_implementation")
    if any(x in lowered for x in ("supabase", "firebase firestore", "prisma")):
        conflicts.append("parallel_persistence_implementation")

    return {
        "schema": "imported_source_analysis.v1",
        "framework": framework,
        "ui_markup": has_ui_markup(paths),
        "files": paths,
        "routes": routes,
        "components": components,
        "data_dependencies": _unique(
            [
                x
                for x in ("fetch", "graphql", "rest" if "/api/" in lowered else "")
                if x and x in lowered
            ]
        ),
        "p2hs_operations_detected": p2hs_ops,
        "external_integrations": integrations,
        "secrets_detected": {"count": secret_count, "redact_before_provider": secret_count > 0},
        "environment_dependencies": env_deps,
        "auth_assumptions": auth_assumptions,
        "backend_assumptions": _unique(
            [
                "server_routes" if "/api/" in lowered else "",
                "direct_browser_fetch" if "fetch(" in lowered else "",
            ]
        ),
        "contract_conflicts": conflicts,
        "composition_requirements": _unique(p2hs_ops),
    }


def build_source_adoption_plan(
    *,
    analyses: list[dict[str, Any]],
    operation: SourceAdoptionMode | None,
    deployment_intent: DeploymentIntent,
    existing_composition: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Create the bounded plan consumed by codegen and verification."""
    operation = operation or "merge"
    flat_ops = _unique(op for a in analyses for op in a.get("p2hs_operations_detected", []))
    externals = [item for a in analyses for item in a.get("external_integrations", [])]
    known = [item for item in externals if item.get("kind") == "known_adapter"]
    custom = [item for item in externals if item.get("kind") == "external"]
    conflicts = _unique(c for a in analyses for c in a.get("contract_conflicts", []))
    secrets = sum(int((a.get("secrets_detected") or {}).get("count", 0)) for a in analyses)
    ui_present = any(a.get("ui_markup") for a in analyses)
    # Say what is actually being preserved. With no markup in the import there is no UI, routes,
    # styling or motion to keep; claiming otherwise made a data-only import look like a full one.
    narrow = operation in ("migrate", "replace") and not ui_present
    return {
        "schema": "source_adoption_plan.v1",
        "operation": operation,
        "deployment_intent": deployment_intent,
        "authority": {
            "platform": "p2hs_security_tenancy_contracts",
            "product": "explicit_user_instruction_and_imported_behavior",
            "implementation": "codegen_preferences",
        },
        "source_fidelity": "full" if ui_present else "content_only",
        "missing_source": [] if ui_present else ["ui_markup"],
        "preserve": ["external_integration_semantics"]
        if narrow
        else [
            "ui",
            "routes",
            "styling",
            "animations",
            "responsive_behavior",
            "external_integration_semantics",
        ],
        "adapt": [
            {"concern": op, "target": _P2HS_TARGETS.get(op, f"p2hs.{op}")} for op in flat_ops
        ],
        "external_bindings": known + custom,
        "remove_or_rewrite": ["hardcoded_secrets", "direct_p2hs_private_endpoints"],
        "configuration_required": [item.get("name") for item in custom]
        + (["imported_secrets"] if secrets else []),
        "contract_conflicts": conflicts,
        "existing_composition": existing_composition or {},
        "verification": {
            "p2hs_contracts": "required",
            "composition": "required",
            "frontend_no_secrets": "required",
            "external_integrations_preserved": "required",
            "build_or_deployability": "required",
        },
    }


def verify_adapted_repository(
    repo_path: str | Path,
    *,
    adoption_plan: dict[str, Any],
    analyses: list[dict[str, Any]],
) -> dict[str, Any]:
    """Verify safety and preservation guarantees before a push."""
    root = Path(repo_path)
    files: list[Path] = []
    for path in root.rglob("*"):
        if not path.is_file() or path.suffix.lower() not in _TEXT_SUFFIXES:
            continue
        if any(
            part in {".git", "node_modules", ".nuxt", ".next", "dist", "build"}
            for part in path.parts
        ):
            continue
        files.append(path)
    contents: dict[str, str] = {}
    for path in files:
        try:
            contents[str(path.relative_to(root))] = path.read_text(
                encoding="utf-8", errors="ignore"
            )
        except OSError:
            continue
    combined = "\n".join(contents.values())
    violations: list[str] = []
    if _SECRET_RE.search(combined):
        violations.append("frontend_contains_hardcoded_secret")
    if re.search(r"https?://(?:api|cloud|internal)\.p2hs\.com", combined, re.I):
        violations.append("frontend_contains_direct_p2hs_private_endpoint")

    environment_dependencies = {
        dependency.lower()
        for analysis in analyses
        for dependency in analysis.get("environment_dependencies", [])
    }
    missing_external: list[str] = []
    for item in adoption_plan.get("external_bindings", []):
        if item.get("kind") != "external":
            continue
        name = str(item.get("name") or "").lower()
        if name and name not in combined.lower() and not environment_dependencies:
            missing_external.append(name)
    if missing_external:
        violations.append("external_integration_semantics_missing:" + ",".join(missing_external))

    result = {
        "schema": "source_adoption_verification.v1",
        "status": "failed" if violations else "passed",
        "files_scanned": len(contents),
        "violations": violations,
        "external_integrations_preserved": not missing_external,
        "p2hs_contracts": "pending",
        "composition": "pending",
    }
    if violations:
        raise ValueError("SOURCE_ADOPTION_VERIFICATION_FAILED:" + ";".join(violations))
    return result


__all__ = [
    "analyze_imported_source",
    "build_source_adoption_plan",
    "redact_source",
    "verify_adapted_repository",
]
