"""Public capability/connector gateway contract.

Dashboard/BFF is the only externally addressed HTTP surface. Core HTTP is
private (session-authenticated, called by the BFF). This module is transport
policy only: it does not resolve capabilities, pick providers, or plan
execution.
"""
from __future__ import annotations

from typing import Any

from src.domain.connectors.resource_contracts import RESOURCE_INPUTS
from src.domain.connectors.invoke_capabilities import CRM_PUBLIC_INVOKE_VERBS

PAYMENT_PUBLIC_INVOKE_VERBS = (
    "payments.checkout.create",
    "payments.order.capture",
    "payments.refund.create",
    "payments.catalog.upsert",
)

PUBLIC_CAPABILITY_INVOKE_TEMPLATE = (
    "POST /api/projects/{project_id}/capabilities/{capability}"
)
PUBLIC_CONNECTOR_ADMIN_PREFIX = "/api/projects/{project_id}/connectors"

# Dashboard/BFF public surfaces that already proxy Core. Capabilities is the
# product/runtime invoke path. The rest is provider administration / linking.
PUBLIC_BFF_SURFACES: tuple[dict[str, str], ...] = (
    {
        "kind": "capabilities",
        "path": "/api/projects/{project_id}/capabilities/{capability}",
        "role": "stable_product_runtime",
        "dash": "server/api/projects/[projectId]/capabilities/",
    },
    {
        "kind": "project_connectors",
        "path": "/api/projects/{project_id}/connectors",
        "role": "provider_admin_advanced",
        "dash": "server/api/projects/[projectId]/connectors/",
        "ui_alias": "/api/builder/projects/{project_id}/connectors",
    },
    {
        "kind": "connector_catalog",
        "path": "/api/connectors",
        "role": "provider_admin_advanced",
        "dash": "server/api/connectors/",
    },
    {
        "kind": "workspace_connector_defaults",
        "path": "/api/workspaces/{workspace_id}/connector-defaults",
        "role": "provider_admin_advanced",
        "dash": "server/api/workspaces/[workspaceId]/connector-defaults",
    },
    {
        "kind": "account_linking",
        "path": "/api/integrations",
        "role": "provider_admin_advanced",
        "dash": "server/api/integrations/",
        "notes": "OAuth/account linking (e.g. Discord). Not a capability invoke surface.",
    },
)

PUBLIC_INVOCABLE_CAPABILITIES = frozenset({
    "email.send",
    "messaging.send",
    "messaging.sms.send",
    "automation.workflow.trigger",
    "document.create",
    "document.get",
    "document.insert_text",
    "document.append_text",
    "document.replace_text",
})

PUBLIC_INVOCABLE_CAPABILITIES = PUBLIC_INVOCABLE_CAPABILITIES | frozenset(RESOURCE_INPUTS)
PUBLIC_INVOCABLE_CAPABILITIES = PUBLIC_INVOCABLE_CAPABILITIES | frozenset(
    CRM_PUBLIC_INVOKE_VERBS
)
PUBLIC_INVOCABLE_CAPABILITIES = PUBLIC_INVOCABLE_CAPABILITIES | frozenset(
    PAYMENT_PUBLIC_INVOKE_VERBS
)

# Staged public proxy before Codegen default. Empty after 2G; SMS stays unproxied.
PUBLIC_BFF_STAGED_CAPABILITIES: frozenset[str] = frozenset()
# SMS remains an internal/provider-bound path. It is invocable by trusted server-side
# capability flows, but it is deliberately not exposed as a public BFF proxy.
PUBLIC_BFF_PROXY_CAPABILITIES = (
    PUBLIC_INVOCABLE_CAPABILITIES - frozenset({"messaging.sms.send"})
) | PUBLIC_BFF_STAGED_CAPABILITIES

# Generated-app installed Cloud job port. Same capability, app-origin BFF → Cloud.
EMAIL_SEND_APP_LOCAL_PORT = "POST /api/jobs/email.send"

FORBIDDEN_RESPONSE_KEYS = frozenset(
    {
        "spreadsheet_id", "spreadsheetId", "sheet_id", "sheetId", "range",
        "bucket_name", "account_id", "endpoint", "access_key_id", "secret_access_key",
        "session_token",
        "action",
        "action_slug",
        "adapter",
        "binding_connection_id",
        "channel",
        "connection_id",
        "connector_key",
        "destination_map",
        "execution_plan",
        "plan",
        "provider",
        "provider_address",
        "provider_hint",
        "thread_ts",
        "twilio_sid",
        "from_number",
        "messaging_service_sid",
        "media_url",
        "sid",
        "webhook_url",
        "hook_url",
        "scenario_id",
        "workflow_id",
        "zap_id",
        "run_id",
        "job_id",
        "connector_id",
        "provider_action",
        "payload",
        "output",
        "result",
        "requests",
        "batchUpdate",
        "credentials",
        "access_token",
        "refresh_token",
        "client_secret",
        "token",
        "document_id",
        "revision_id",
        "google_document_id",
        "docs_document_id",
        "documentId",
        "revisionId",
        "contact_id",
        "hs_object_id",
        "already_exists",
        "filter_groups",
        "instance_url",
        "login_host",
        "org_id",
        "portal_id",
        "portalId",
        "soql",
        "after",
        "nextRecordsUrl",
        "queryLocator",
        "vid",
        "company_id",
        "Website",
        "deal_id",
        "opportunity_id",
        "RecordTypeId",
        "association_type_id",
        "association_category",
        "association_id",
        "AccountId",
        "lookup_field",
        "WhoId",
        "WhatId",
        "OpportunityContactRole",
        "dealstage",
        "StageName",
    }
)

FORBIDDEN_CODEGEN_CORE_MARKERS = (
    "beapipie.p2hs.com",
    "stag1.piejet.com",
    "P2HS_BACKEND_URL",
    "CORE_API_BASE_URL",
    "cloud_control_api_url",
)

BFF_MAY_OWN = (
    "session_authentication",
    "project_access",
    "edge_request_validation",
    "csrf_origin_rate_limit",
    "proxying",
    "normalized_public_http_response",
)

BFF_MUST_NOT = (
    "capability_resolution",
    "provider_selection",
    "binding_semantics",
    "connector_permissions",
    "execution_planning",
    "provider_authorization_policy",
    "document_alias_resolution",
    "google_payload_normalization",
    "persist_create_binding",
    "stage_map_lookup",
    "pipeline_map_lookup",
    "soql_generation",
    "hubspot_filter_generation",
    "accountid_mapping",
    "association_type_selection",
    "provider_error_translation",
)


def public_capability_invoke_path(project_id: str, capability: str) -> str:
    """Platform-relative public path. No host. Routing stays outside generated code."""
    return f"/api/projects/{project_id}/capabilities/{capability}"


def public_capability_invoke_method(capability: str) -> str:
    return PUBLIC_CAPABILITY_INVOKE_TEMPLATE.format(
        project_id="{project_id}",
        capability=capability,
    )


def is_public_invocable_capability(capability: str) -> bool:
    return capability in PUBLIC_INVOCABLE_CAPABILITIES


def is_public_bff_proxy_capability(capability: str) -> bool:
    return capability in PUBLIC_BFF_PROXY_CAPABILITIES


def strip_capability_response(body: Any) -> Any:
    """Drop execution-plan internals. Do not rewrite reason codes."""
    if not isinstance(body, dict):
        return body
    if isinstance(body.get("detail"), dict):
        nested = dict(body)
        nested["detail"] = strip_capability_response(body["detail"])
        return {k: v for k, v in nested.items() if k not in FORBIDDEN_RESPONSE_KEYS}
    return {k: v for k, v in body.items() if k not in FORBIDDEN_RESPONSE_KEYS}


def invoke_path_has_host(method_or_path: str) -> bool:
    lowered = method_or_path.lower()
    return "://" in lowered or lowered.startswith("http")


PHASE_0_STATUS = "complete"
PHASE_0A_STATUS = "complete"
PHASE_1A_STATUS = "frozen"
PHASE_1B_STATUS = "complete"
PHASE_1C_STATUS = "complete"
PHASE_1D_STATUS = "blocked"
PHASE_2A_STATUS = "frozen"
PHASE_2P_STATUS = "complete"
PHASE_2B_STATUS = "frozen"
PHASE_2C_STATUS = "complete"
PHASE_2D_STATUS = "complete"
PHASE_2E_STATUS = "complete"
PHASE_2F_STATUS = "complete"
PHASE_2G_STATUS = "complete"
PHASE_3_STATUS = "complete"
PHASE_3A_STATUS = "next"
PHASE_4_STATUS = "complete"
PHASE_5_STATUS = "complete"
PHASE_6_STATUS = "6p_complete"
PHASE_7_STATUS = "complete"
# Capability semantics ≠ one transport. Email keeps two provider-neutral runtimes.
EMAIL_SEND_TRANSPORTS: tuple[str, ...] = (
    "dashboard_bff:/api/projects/{project_id}/capabilities/email.send",
    "installed_app:/api/jobs/email.send",
)
LEGACY_CAPABILITY_ALIGNMENT: dict[str, dict[str, Any]] = {
    "email.send": {
        "status": "aligned_dual_runtime_exception",
        "public_dashboard_bff": True,
        "core": "private_resolver_policy",
        "cloud": "email.send_job",
        "codegen": "capability",
        "provider_action": "escape_hatch",
        "no_jobs_imitation_required": True,
    },
    "messaging.send": {
        "status": "aligned",
        "public_dashboard_bff": True,
        "core": "private_resolver_policy",
        "cloud": "integration.execute",
        "codegen": "capability",
        "provider_action": "escape_hatch",
        "builder_alias_only": True,
        "integrations_are_account_linking": True,
    },
    "document.create": {
        "status": "aligned",
        "public_dashboard_bff": True,
        "core": "private_resolver_policy",
        "cloud": "integration.execute",
        "codegen": "capability",
        "provider_action": "escape_hatch",
        "create_binding": "deferred_do_not_assume_alias",
    },
    "messaging.sms.send": {
        "status": "aligned_internal_only",
        "public_dashboard_bff": False,
        "core": "private_resolver_policy",
        "cloud": "integration.execute",
        "codegen": "capability",
        "provider_action": "current_primary_path",
        "future_public": "dashboard_bff_not_jobs",
        "no_jobs_route": True,
    },
}
PHASE_0_INVARIANTS: dict[str, str] = {
    "public_edge": "dashboard_bff",
    "private_policy_engine": "core",
    "private_execution_engine": "cloud",
    "capability_invoke": "/api/projects/{project_id}/capabilities/*",
    "connector_admin_canonical": "/api/projects/{project_id}/connectors/*",
    "connector_admin_ui_alias": "/api/builder/projects/{project_id}/connectors/*",
    "connector_catalog": "/api/connectors",
    "workspace_defaults": "/api/workspaces/{workspace_id}/connector-defaults",
    "account_linking": "/api/integrations/*",
    "integrations_layer": "lifecycle_not_runtime",
    "email_send_dual_path": "dashboard_capability_and_generated_app_jobs",
    "email_send_jobs_is_not_core_url": "true",
    "public_response_owner": "core",
    "bff_strip": "defense_in_depth",
    "codegen": "host_relative_urls_only",
    "managed_vs_customer": "parallel_independent_domains_not_fallbacks",
}

PUBLIC_RESPONSE_OWNER = "core"
BFF_STRIP_ROLE = "defense_in_depth"
