from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from codex_a2a.config import Settings

COMPATIBILITY_PROFILE_ID = "codex-a2a-single-tenant-coding-v1"
DEPLOYMENT_PROFILE_ID = "single_tenant_shared_workspace"


@dataclass(frozen=True)
class DeploymentProfile:
    id: str
    single_tenant: bool
    shared_workspace_across_consumers: bool
    tenant_isolation: str

    def as_dict(self) -> dict[str, Any]:
        return {
            "id": self.id,
            "single_tenant": self.single_tenant,
            "shared_workspace_across_consumers": self.shared_workspace_across_consumers,
            "tenant_isolation": self.tenant_isolation,
        }


@dataclass(frozen=True)
class DirectoryBindingProfile:
    allow_override: bool
    scope: str

    def as_dict(self) -> dict[str, Any]:
        return {
            "allow_override": self.allow_override,
            "scope": self.scope,
        }


@dataclass(frozen=True)
class ConditionalSurfaceProfile:
    enabled: bool
    availability: str
    toggle: str

    def as_dict(self) -> dict[str, Any]:
        return {
            "enabled": self.enabled,
            "availability": self.availability,
            "toggle": self.toggle,
        }


@dataclass(frozen=True)
class InterruptProfile:
    request_ttl_seconds: int

    def as_dict(self) -> dict[str, Any]:
        return {
            "request_ttl_seconds": self.request_ttl_seconds,
        }


@dataclass(frozen=True)
class ServiceFeaturesProfile:
    streaming: dict[str, Any]
    health_endpoint: dict[str, Any]

    def as_dict(self) -> dict[str, Any]:
        return {
            "streaming": dict(self.streaming),
            "health_endpoint": dict(self.health_endpoint),
        }


@dataclass(frozen=True)
class SandboxProfile:
    mode: str
    filesystem_scope: str
    writable_roots: tuple[str, ...] = ()

    def as_dict(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "mode": self.mode,
            "filesystem_scope": self.filesystem_scope,
        }
        if self.writable_roots:
            payload["writable_roots"] = list(self.writable_roots)
        return payload


@dataclass(frozen=True)
class NetworkProfile:
    access: str
    allowed_domains: tuple[str, ...] = ()

    def as_dict(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "access": self.access,
        }
        if self.allowed_domains:
            payload["allowed_domains"] = list(self.allowed_domains)
        return payload


@dataclass(frozen=True)
class ApprovalProfile:
    policy: str
    escalation_behavior: str | None = None

    def as_dict(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "policy": self.policy,
        }
        if self.escalation_behavior is not None:
            payload["escalation_behavior"] = self.escalation_behavior
        return payload


@dataclass(frozen=True)
class WriteAccessProfile:
    scope: str
    outside_workspace: bool | None = None

    def as_dict(self) -> dict[str, Any]:
        payload: dict[str, Any] = {
            "scope": self.scope,
        }
        if self.outside_workspace is not None:
            payload["outside_workspace"] = self.outside_workspace
        return payload


@dataclass(frozen=True)
class ExecutionEnvironmentProfile:
    sandbox: SandboxProfile
    network: NetworkProfile
    approval: ApprovalProfile
    write_access: WriteAccessProfile

    def as_dict(self) -> dict[str, Any]:
        return {
            "sandbox": self.sandbox.as_dict(),
            "network": self.network.as_dict(),
            "approval": self.approval.as_dict(),
            "write_access": self.write_access.as_dict(),
        }


@dataclass(frozen=True)
class RuntimeContext:
    project: str | None = None
    workspace_root: str | None = None
    provider_id: str | None = None
    model_id: str | None = None
    agent: str | None = None
    variant: str | None = None

    def as_dict(self) -> dict[str, str]:
        context: dict[str, str] = {}
        if self.project:
            context["project"] = self.project
        if self.workspace_root:
            context["workspace_root"] = self.workspace_root
        if self.provider_id:
            context["provider_id"] = self.provider_id
        if self.model_id:
            context["model_id"] = self.model_id
        if self.agent:
            context["agent"] = self.agent
        if self.variant:
            context["variant"] = self.variant
        return context


@dataclass(frozen=True)
class RuntimeProfile:
    profile_id: str
    deployment: DeploymentProfile
    directory_binding: DirectoryBindingProfile
    session_shell: ConditionalSurfaceProfile
    turn_control: ConditionalSurfaceProfile
    review_control: ConditionalSurfaceProfile
    exec_control: ConditionalSurfaceProfile
    interrupts: InterruptProfile
    service_features: ServiceFeaturesProfile
    execution_environment: ExecutionEnvironmentProfile
    runtime_context: RuntimeContext

    @property
    def session_shell_enabled(self) -> bool:
        return self.session_shell.enabled

    @property
    def turn_control_enabled(self) -> bool:
        return self.turn_control.enabled

    @property
    def review_control_enabled(self) -> bool:
        return self.review_control.enabled

    @property
    def exec_control_enabled(self) -> bool:
        return self.exec_control.enabled

    def runtime_features_dict(self) -> dict[str, Any]:
        return {
            "directory_binding": self.directory_binding.as_dict(),
            "session_shell": self.session_shell.as_dict(),
            "turn_control": self.turn_control.as_dict(),
            "review_control": self.review_control.as_dict(),
            "exec_control": self.exec_control.as_dict(),
            "interrupts": self.interrupts.as_dict(),
            "service_features": self.service_features.as_dict(),
            "execution_environment": self.execution_environment.as_dict(),
        }

    def summary_dict(self) -> dict[str, Any]:
        profile = {
            "profile_id": self.profile_id,
            "deployment": self.deployment.as_dict(),
            "runtime_features": self.runtime_features_dict(),
        }
        runtime_context = self.runtime_context.as_dict()
        if runtime_context:
            profile["runtime_context"] = runtime_context
        return profile

    def health_payload(self, *, service: str, version: str) -> dict[str, Any]:
        return {
            "status": "ok",
            "service": service,
            "version": version,
            "profile": self.summary_dict(),
        }


def _default_filesystem_scope(*, sandbox_mode: str) -> str:
    if sandbox_mode == "read-only":
        return "none"
    if sandbox_mode == "workspace-write":
        return "workspace_root_or_descendant"
    if sandbox_mode == "danger-full-access":
        return "full_filesystem"
    return "unknown"


def _default_approval_escalation_behavior(*, policy: str) -> str | None:
    if policy == "never":
        return "unavailable"
    if policy == "on-request":
        return "per_request"
    if policy == "on-failure":
        return "fallback_only"
    if policy == "untrusted-only":
        return "restricted"
    return None


def _default_write_access_scope(*, sandbox_mode: str, filesystem_scope: str) -> str:
    if sandbox_mode == "read-only":
        return "none"
    if sandbox_mode == "danger-full-access":
        return "full_filesystem"
    if filesystem_scope in {
        "none",
        "workspace_root",
        "workspace_root_or_descendant",
        "configured_roots",
        "full_filesystem",
    }:
        return filesystem_scope
    return "unknown"


def _default_write_outside_workspace(*, write_access_scope: str) -> bool | None:
    if write_access_scope == "full_filesystem":
        return True
    if write_access_scope in {"none", "workspace_root", "workspace_root_or_descendant"}:
        return False
    return None


def build_runtime_profile(settings: Settings) -> RuntimeProfile:
    deployment = DeploymentProfile(
        id=DEPLOYMENT_PROFILE_ID,
        single_tenant=True,
        shared_workspace_across_consumers=True,
        tenant_isolation="none",
    )
    directory_scope = (
        "workspace_root_or_descendant"
        if settings.a2a_allow_directory_override
        else "workspace_root_only"
    )
    sandbox_filesystem_scope = (
        settings.a2a_execution_sandbox_filesystem_scope
        or _default_filesystem_scope(sandbox_mode=settings.a2a_execution_sandbox_mode)
    )
    approval_escalation_behavior = (
        settings.a2a_execution_approval_escalation_behavior
        if settings.a2a_execution_approval_escalation_behavior is not None
        else _default_approval_escalation_behavior(policy=settings.a2a_execution_approval_policy)
    )
    write_access_scope = settings.a2a_execution_write_access_scope or _default_write_access_scope(
        sandbox_mode=settings.a2a_execution_sandbox_mode,
        filesystem_scope=sandbox_filesystem_scope,
    )
    write_outside_workspace = (
        settings.a2a_execution_write_outside_workspace
        if settings.a2a_execution_write_outside_workspace is not None
        else _default_write_outside_workspace(write_access_scope=write_access_scope)
    )
    return RuntimeProfile(
        profile_id=COMPATIBILITY_PROFILE_ID,
        deployment=deployment,
        directory_binding=DirectoryBindingProfile(
            allow_override=settings.a2a_allow_directory_override,
            scope=directory_scope,
        ),
        session_shell=ConditionalSurfaceProfile(
            enabled=settings.a2a_enable_session_shell,
            availability="enabled" if settings.a2a_enable_session_shell else "disabled",
            toggle="A2A_ENABLE_SESSION_SHELL",
        ),
        turn_control=ConditionalSurfaceProfile(
            enabled=settings.a2a_enable_turn_control,
            availability="enabled" if settings.a2a_enable_turn_control else "disabled",
            toggle="A2A_ENABLE_TURN_CONTROL",
        ),
        review_control=ConditionalSurfaceProfile(
            enabled=settings.a2a_enable_review_control,
            availability="enabled" if settings.a2a_enable_review_control else "disabled",
            toggle="A2A_ENABLE_REVIEW_CONTROL",
        ),
        exec_control=ConditionalSurfaceProfile(
            enabled=settings.a2a_enable_exec_control,
            availability="enabled" if settings.a2a_enable_exec_control else "disabled",
            toggle="A2A_ENABLE_EXEC_CONTROL",
        ),
        interrupts=InterruptProfile(
            request_ttl_seconds=settings.a2a_interrupt_request_ttl_seconds,
        ),
        service_features=ServiceFeaturesProfile(
            streaming={"enabled": True, "availability": "always"},
            health_endpoint={
                "enabled": settings.a2a_enable_health_endpoint,
                "availability": ("enabled" if settings.a2a_enable_health_endpoint else "disabled"),
                "toggle": "A2A_ENABLE_HEALTH_ENDPOINT",
            },
        ),
        execution_environment=ExecutionEnvironmentProfile(
            sandbox=SandboxProfile(
                mode=settings.a2a_execution_sandbox_mode,
                filesystem_scope=sandbox_filesystem_scope,
                writable_roots=tuple(settings.a2a_execution_sandbox_writable_roots),
            ),
            network=NetworkProfile(
                access=settings.a2a_execution_network_access,
                allowed_domains=tuple(settings.a2a_execution_network_allowed_domains),
            ),
            approval=ApprovalProfile(
                policy=settings.a2a_execution_approval_policy,
                escalation_behavior=approval_escalation_behavior,
            ),
            write_access=WriteAccessProfile(
                scope=write_access_scope,
                outside_workspace=write_outside_workspace,
            ),
        ),
        runtime_context=RuntimeContext(
            project=settings.a2a_project,
            workspace_root=settings.codex_workspace_root,
            provider_id=settings.codex_provider_id,
            model_id=settings.codex_model_id,
            agent=settings.codex_agent,
            variant=settings.codex_variant,
        ),
    )
