import uuid

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from .computer_catalog import BUILTIN_COMPUTER_TEMPLATES
from .errors import AppError
from .models import Agent, Computer, ComputerNetworkPolicy, ComputerProfile, ComputerTemplate


def build_computer_templates(workspace_id: uuid.UUID) -> list[ComputerTemplate]:
    return [
        ComputerTemplate(
            id=uuid.uuid4(),
            workspace_id=workspace_id,
            template_key=item.key,
            name=item.name,
            os_family=item.os_family,
            os_distribution=item.distribution,
            os_version=item.version,
            browser=item.browser,
            minimum_cpu=item.minimum_cpu,
            minimum_memory_mb=item.minimum_memory_mb,
            minimum_disk_gb=item.minimum_disk_gb,
            windows_license_required=item.windows_license_required,
            status="unconfigured",
        )
        for item in BUILTIN_COMPUTER_TEMPLATES
    ]


async def computer_template_for_key(
    session: AsyncSession, *, workspace_id: uuid.UUID, template_key: str
) -> ComputerTemplate:
    template = await session.scalar(select(ComputerTemplate).where(
        ComputerTemplate.workspace_id == workspace_id,
        ComputerTemplate.template_key == template_key,
        ComputerTemplate.status != "disabled",
    ))
    if not template:
        raise AppError("COMPUTER_TEMPLATE_NOT_FOUND", "The OS template is unavailable.", 422)
    return template


def build_private_computer(
    *,
    workspace_id: uuid.UUID,
    agent_id: uuid.UUID,
    computer_id: uuid.UUID,
    created_by_user_id: uuid.UUID,
    agent_name: str,
    active: bool,
) -> ComputerProfile:
    """Create the browser identity inside an agent's dedicated computer."""
    return ComputerProfile(
        id=uuid.uuid4(),
        workspace_id=workspace_id,
        agent_id=agent_id,
        computer_id=computer_id,
        created_by_user_id=created_by_user_id,
        profile_key=f"agent-{agent_id.hex}",
        name=f"{agent_name} - Private Computer"[:160],
        storage_key=uuid.uuid4().hex,
        status="active" if active else "disabled",
        retention_days=30,
        version=1,
    )


def build_dedicated_computer(
    *,
    workspace_id: uuid.UUID,
    created_by_user_id: uuid.UUID,
    agent_name: str,
    template: ComputerTemplate,
    compute_provider: str,
    cpu_cores: int,
    memory_mb: int,
    disk_gb: int,
    browser: str,
    persistent_disk: bool,
    system_privilege: str,
    start_policy: str,
    network_policy: dict,
) -> tuple[Computer, ComputerNetworkPolicy]:
    """Build durable control-plane records; this does not pretend a VM was provisioned."""
    computer_id = uuid.uuid4()
    provider_ready = compute_provider != "unconfigured" and template.status == "available"
    computer = Computer(
        id=computer_id,
        workspace_id=workspace_id,
        template_id=template.id,
        created_by_user_id=created_by_user_id,
        name=f"{agent_name} - Private Computer"[:160],
        os_family=template.os_family,
        os_distribution=template.os_distribution,
        os_version=template.os_version,
        compute_provider=compute_provider,
        cpu_cores=cpu_cores,
        memory_mb=memory_mb,
        disk_gb=disk_gb,
        browser=browser,
        persistent_disk=persistent_disk,
        system_privilege=system_privilege,
        start_policy=start_policy,
        status="pending" if provider_ready else "unconfigured",
        desired_state="stopped",
        version=1,
    )
    policy = ComputerNetworkPolicy(
        id=uuid.uuid4(),
        workspace_id=workspace_id,
        computer_id=computer_id,
        internet_access=network_policy.get("internet_access", True),
        lan_access=network_policy.get("lan_access", False),
        platform_api_access=network_policy.get("platform_api_access", True),
        other_agent_networks=network_policy.get("other_agent_networks", False),
        host_management_access=False,
        allowed_domains=network_policy.get("allowed_domains", []),
        blocked_domains=network_policy.get("blocked_domains", []),
        version=1,
    )
    return computer, policy


async def private_computer_for_agent(
    session: AsyncSession,
    *,
    workspace_id: uuid.UUID,
    agent_id: uuid.UUID,
    lock: bool = False,
) -> ComputerProfile:
    statement = select(ComputerProfile).where(
        ComputerProfile.workspace_id == workspace_id,
        ComputerProfile.agent_id == agent_id,
    )
    if lock:
        statement = statement.with_for_update()
    profile = await session.scalar(statement)
    if not profile:
        raise AppError(
            "AGENT_PRIVATE_COMPUTER_MISSING",
            "The agent's private computer is unavailable.",
            409,
        )
    return profile


async def dedicated_computer_for_agent(
    session: AsyncSession,
    *,
    workspace_id: uuid.UUID,
    agent_id: uuid.UUID,
    lock: bool = False,
) -> Computer:
    statement = (
        select(Computer)
        .join(Agent, Agent.computer_id == Computer.id)
        .where(
            Computer.workspace_id == workspace_id,
            Agent.workspace_id == workspace_id,
            Agent.id == agent_id,
        )
    )
    if lock:
        statement = statement.with_for_update()
    computer = await session.scalar(statement)
    if not computer:
        raise AppError(
            "AGENT_PRIVATE_COMPUTER_MISSING",
            "The agent's dedicated computer is unavailable.",
            409,
        )
    return computer
