"""dedicated agent virtual computers

Revision ID: c31f8b7d4a20
Revises: d7e5a9206c13
"""

import uuid
from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

revision: str = "c31f8b7d4a20"
down_revision: str | Sequence[str] | None = "d7e5a9206c13"
branch_labels = None
depends_on = None

JSON_TYPE = sa.JSON().with_variant(postgresql.JSONB(astext_type=sa.Text()), "postgresql")
TEMPLATES = (
    ("ubuntu-desktop-24-04", "Ubuntu Desktop 24.04 LTS", "linux", "Ubuntu Desktop",
     "24.04 LTS", "chromium", 2, 4096, 40, False),
    ("ubuntu-server-desktop-24-04", "Ubuntu Server 24.04 LTS + Desktop", "linux",
     "Ubuntu Server + Desktop", "24.04 LTS", "chromium", 2, 4096, 30, False),
    ("debian-desktop-13", "Debian Desktop", "linux", "Debian Desktop", "13",
     "chromium", 2, 4096, 30, False),
    ("windows-11", "Windows 11", "windows", "Windows", "11", "edge", 4, 8192,
     80, True),
    ("windows-server", "Windows Server", "windows", "Windows Server", "2025", "edge",
     4, 8192, 80, True),
)


def upgrade() -> None:
    op.create_table(
        "computer_templates",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("template_key", sa.String(length=80), nullable=False),
        sa.Column("name", sa.String(length=160), nullable=False),
        sa.Column("os_family", sa.String(length=16), nullable=False),
        sa.Column("os_distribution", sa.String(length=80), nullable=False),
        sa.Column("os_version", sa.String(length=80), nullable=False),
        sa.Column("browser", sa.String(length=40), nullable=False),
        sa.Column("provider_template_ref", sa.String(length=255), nullable=True),
        sa.Column("image_digest", sa.String(length=160), nullable=True),
        sa.Column("minimum_cpu", sa.Integer(), nullable=False),
        sa.Column("minimum_memory_mb", sa.Integer(), nullable=False),
        sa.Column("minimum_disk_gb", sa.Integer(), nullable=False),
        sa.Column("windows_license_required", sa.Boolean(), nullable=False),
        sa.Column("status", sa.String(length=24), nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint("minimum_cpu >= 1", name="ck_template_minimum_cpu"),
        sa.CheckConstraint("minimum_disk_gb >= 10", name="ck_template_minimum_disk"),
        sa.CheckConstraint("minimum_memory_mb >= 512", name="ck_template_minimum_memory"),
        sa.CheckConstraint("os_family IN ('linux','windows')", name="ck_template_os_family"),
        sa.CheckConstraint(
            "status IN ('available','unconfigured','disabled')", name="ck_template_status"
        ),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"]),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("workspace_id", "id", name="uq_computer_template_workspace_id"),
        sa.UniqueConstraint(
            "workspace_id", "template_key", name="uq_computer_template_workspace_key"
        ),
    )
    op.create_index(
        op.f("ix_computer_templates_workspace_id"), "computer_templates", ["workspace_id"]
    )
    op.create_table(
        "computers",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("template_id", sa.Uuid(), nullable=False),
        sa.Column("created_by_user_id", sa.Uuid(), nullable=False),
        sa.Column("name", sa.String(length=160), nullable=False),
        sa.Column("os_family", sa.String(length=16), nullable=False),
        sa.Column("os_distribution", sa.String(length=80), nullable=False),
        sa.Column("os_version", sa.String(length=80), nullable=False),
        sa.Column("compute_provider", sa.String(length=40), nullable=False),
        sa.Column("provider_resource_id", sa.String(length=255), nullable=True),
        sa.Column("cpu_cores", sa.Integer(), nullable=False),
        sa.Column("memory_mb", sa.Integer(), nullable=False),
        sa.Column("disk_gb", sa.Integer(), nullable=False),
        sa.Column("gpu", sa.String(length=120), nullable=True),
        sa.Column("browser", sa.String(length=40), nullable=False),
        sa.Column("persistent_disk", sa.Boolean(), nullable=False),
        sa.Column("system_privilege", sa.String(length=24), nullable=False),
        sa.Column("start_policy", sa.String(length=24), nullable=False),
        sa.Column("status", sa.String(length=24), nullable=False),
        sa.Column("desired_state", sa.String(length=16), nullable=False),
        sa.Column("ip_address", sa.String(length=64), nullable=True),
        sa.Column("control_endpoint", sa.String(length=255), nullable=True),
        sa.Column("browser_status", sa.String(length=40), nullable=True),
        sa.Column("current_application", sa.String(length=160), nullable=True),
        sa.Column("current_task", sa.String(length=500), nullable=True),
        sa.Column("provider_error_code", sa.String(length=80), nullable=True),
        sa.Column("provider_error_message", sa.String(length=500), nullable=True),
        sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("stopped_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("version", sa.Integer(), nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint("cpu_cores BETWEEN 1 AND 64", name="ck_computer_cpu"),
        sa.CheckConstraint("disk_gb BETWEEN 10 AND 16384", name="ck_computer_disk"),
        sa.CheckConstraint(
            "desired_state IN ('running','suspended','stopped','destroyed')",
            name="ck_computer_desired_state",
        ),
        sa.CheckConstraint(
            "memory_mb BETWEEN 512 AND 524288", name="ck_computer_memory"
        ),
        sa.CheckConstraint("os_family IN ('linux','windows')", name="ck_computer_os_family"),
        sa.CheckConstraint(
            "start_policy IN ('always_running','start_when_needed','scheduled','manual')",
            name="ck_computer_start_policy",
        ),
        sa.CheckConstraint(
            "status IN ('unconfigured','pending','provisioning','starting','running','idle',"
            "'suspending','suspended','stopping','stopped','restarting','recovering','failed',"
            "'destroying','destroyed')",
            name="ck_computer_status",
        ),
        sa.CheckConstraint(
            "system_privilege IN ('restricted','standard_user','administrator','full_system')",
            name="ck_computer_system_privilege",
        ),
        sa.CheckConstraint("version > 0", name="ck_computer_version"),
        sa.ForeignKeyConstraint(
            ["workspace_id", "created_by_user_id"],
            ["workspace_memberships.workspace_id", "workspace_memberships.user_id"],
            name="fk_computer_creator",
        ),
        sa.ForeignKeyConstraint(
            ["workspace_id", "template_id"],
            ["computer_templates.workspace_id", "computer_templates.id"],
            name="fk_computer_template",
        ),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"]),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("workspace_id", "id", name="uq_computer_workspace_id"),
        sa.UniqueConstraint("workspace_id", "name", name="uq_computer_workspace_name"),
        sa.UniqueConstraint(
            "workspace_id", "provider_resource_id", name="uq_computer_provider_resource"
        ),
    )
    op.create_index(op.f("ix_computers_workspace_id"), "computers", ["workspace_id"])
    with op.batch_alter_table("agents") as batch:
        batch.add_column(sa.Column("computer_required", sa.Boolean(), nullable=False,
                                   server_default=sa.true()))
        batch.add_column(sa.Column("computer_id", sa.Uuid(), nullable=True))
        batch.add_column(sa.Column("system_privilege", sa.String(length=24), nullable=False,
                                   server_default="standard_user"))
        batch.add_column(sa.Column("computer_autonomy", sa.Integer(), nullable=False,
                                   server_default="1"))
        batch.add_column(sa.Column("computer_network_policy", JSON_TYPE, nullable=False,
                                   server_default=sa.text("'{}'")))
        batch.add_column(sa.Column("computer_file_policy", JSON_TYPE, nullable=False,
                                   server_default=sa.text("'{}'")))
    op.create_index(op.f("ix_agents_computer_id"), "agents", ["computer_id"])
    with op.batch_alter_table("computer_profiles") as batch:
        batch.add_column(sa.Column("agent_id", sa.Uuid(), nullable=True))
        batch.add_column(sa.Column("computer_id", sa.Uuid(), nullable=True))
    op.create_index(op.f("ix_computer_profiles_agent_id"), "computer_profiles", ["agent_id"])
    op.create_index(
        op.f("ix_computer_profiles_computer_id"), "computer_profiles", ["computer_id"]
    )
    _create_supporting_tables()
    _seed_and_backfill()
    with op.batch_alter_table("agents") as batch:
        batch.create_foreign_key(
            "fk_agent_computer", "computers", ["workspace_id", "computer_id"],
            ["workspace_id", "id"],
        )
        batch.create_unique_constraint(
            "uq_agent_dedicated_computer", ["workspace_id", "computer_id"]
        )
        batch.create_check_constraint(
            "ck_agent_computer_autonomy", "computer_autonomy BETWEEN 0 AND 5"
        )
        batch.create_check_constraint(
            "ck_agent_system_privilege",
            "system_privilege IN ('restricted','standard_user','administrator','full_system')",
        )
        batch.create_check_constraint(
            "ck_agent_required_computer", "computer_required = false OR computer_id IS NOT NULL"
        )
        batch.create_check_constraint(
            "ck_active_agent_has_computer", "status != 'active' OR computer_id IS NOT NULL"
        )
        batch.alter_column("computer_required", server_default=None)
        batch.alter_column("system_privilege", server_default=None)
        batch.alter_column("computer_autonomy", server_default=None)
        batch.alter_column("computer_network_policy", server_default=None)
        batch.alter_column("computer_file_policy", server_default=None)
    with op.batch_alter_table("computer_profiles") as batch:
        batch.create_foreign_key(
            "fk_computer_profile_agent", "agents", ["workspace_id", "agent_id"],
            ["workspace_id", "id"],
        )
        batch.create_foreign_key(
            "fk_computer_profile_computer", "computers", ["workspace_id", "computer_id"],
            ["workspace_id", "id"],
        )
        batch.create_unique_constraint(
            "uq_computer_profile_agent", ["workspace_id", "agent_id"]
        )
        batch.create_unique_constraint(
            "uq_computer_profile_computer", ["workspace_id", "computer_id"]
        )
        batch.create_check_constraint(
            "ck_computer_profile_owner", "agent_id IS NOT NULL OR status = 'revoked'"
        )
        batch.create_check_constraint(
            "ck_computer_profile_computer_owner",
            "computer_id IS NOT NULL OR status = 'revoked'",
        )
    op.create_index(
        "uq_computer_active_session_profile",
        "computer_sessions",
        ["workspace_id", "profile_id"],
        unique=True,
        postgresql_where=sa.text(
            "status IN ('starting','ready','ai_controlled','human_controlled',"
            "'awaiting_human','paused','recovering','stopping')"
        ),
        sqlite_where=sa.text(
            "status IN ('starting','ready','ai_controlled','human_controlled',"
            "'awaiting_human','paused','recovering','stopping')"
        ),
    )


def _create_supporting_tables() -> None:
    op.create_table(
        "computer_network_policies",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("computer_id", sa.Uuid(), nullable=False),
        sa.Column("internet_access", sa.Boolean(), nullable=False),
        sa.Column("lan_access", sa.Boolean(), nullable=False),
        sa.Column("platform_api_access", sa.Boolean(), nullable=False),
        sa.Column("other_agent_networks", sa.Boolean(), nullable=False),
        sa.Column("host_management_access", sa.Boolean(), nullable=False),
        sa.Column("allowed_domains", JSON_TYPE, nullable=False),
        sa.Column("blocked_domains", JSON_TYPE, nullable=False),
        sa.Column("version", sa.Integer(), nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint(
            "host_management_access = false", name="ck_computer_network_no_host_management"
        ),
        sa.ForeignKeyConstraint(
            ["workspace_id", "computer_id"], ["computers.workspace_id", "computers.id"],
            name="fk_computer_network_policy_computer", ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "workspace_id", "computer_id", name="uq_computer_network_policy_computer"
        ),
    )
    op.create_index(
        op.f("ix_computer_network_policies_workspace_id"), "computer_network_policies",
        ["workspace_id"],
    )
    op.create_index(
        op.f("ix_computer_network_policies_computer_id"), "computer_network_policies",
        ["computer_id"],
    )
    op.create_table(
        "workspace_computer_limits",
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("maximum_computers", sa.Integer(), nullable=False),
        sa.Column("maximum_running_computers", sa.Integer(), nullable=False),
        sa.Column("maximum_cpu_per_computer", sa.Integer(), nullable=False),
        sa.Column("maximum_memory_mb", sa.Integer(), nullable=False),
        sa.Column("maximum_disk_gb", sa.Integer(), nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint("maximum_computers >= 1", name="ck_computer_limit_count"),
        sa.CheckConstraint("maximum_cpu_per_computer >= 1", name="ck_computer_limit_cpu"),
        sa.CheckConstraint("maximum_disk_gb >= 10", name="ck_computer_limit_disk"),
        sa.CheckConstraint("maximum_memory_mb >= 512", name="ck_computer_limit_memory"),
        sa.CheckConstraint(
            "maximum_running_computers >= 1", name="ck_computer_limit_running"
        ),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"]),
        sa.PrimaryKeyConstraint("workspace_id"),
    )
    op.create_table(
        "computer_operations",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("computer_id", sa.Uuid(), nullable=False),
        sa.Column("requested_by_user_id", sa.Uuid(), nullable=False),
        sa.Column("operation", sa.String(length=24), nullable=False),
        sa.Column("risk", sa.String(length=16), nullable=False),
        sa.Column("idempotency_key", sa.String(length=180), nullable=False),
        sa.Column("request_hash", sa.String(length=64), nullable=False),
        sa.Column("request_redacted", JSON_TYPE, nullable=False),
        sa.Column("status", sa.String(length=24), nullable=False),
        sa.Column("provider_request_id", sa.String(length=160), nullable=True),
        sa.Column("verification", JSON_TYPE, nullable=True),
        sa.Column("error_code", sa.String(length=80), nullable=True),
        sa.Column("error_message", sa.String(length=500), nullable=True),
        sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint(
            "operation IN ('create','start','stop','restart','pause','resume','destroy',"
            "'snapshot','restore','resize','refresh')", name="ck_computer_operation_name",
        ),
        sa.CheckConstraint(
            "status IN ('pending','running','succeeded','failed','blocked','unknown')",
            name="ck_computer_operation_status",
        ),
        sa.CheckConstraint("risk IN ('low','medium','high')", name="ck_computer_operation_risk"),
        sa.ForeignKeyConstraint(
            ["workspace_id", "computer_id"], ["computers.workspace_id", "computers.id"],
            name="fk_computer_operation_computer", ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["workspace_id", "requested_by_user_id"],
            ["workspace_memberships.workspace_id", "workspace_memberships.user_id"],
            name="fk_computer_operation_requester",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("workspace_id", "id", name="uq_computer_operation_workspace_id"),
        sa.UniqueConstraint(
            "workspace_id", "idempotency_key", name="uq_computer_operation_idempotency"
        ),
    )
    op.create_index(op.f("ix_computer_operations_workspace_id"), "computer_operations",
                    ["workspace_id"])
    op.create_index(op.f("ix_computer_operations_computer_id"), "computer_operations",
                    ["computer_id"])
    op.create_table(
        "computer_snapshots",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("computer_id", sa.Uuid(), nullable=False),
        sa.Column("created_by_user_id", sa.Uuid(), nullable=False),
        sa.Column("name", sa.String(length=160), nullable=False),
        sa.Column("provider_snapshot_id", sa.String(length=255), nullable=True),
        sa.Column("status", sa.String(length=24), nullable=False),
        sa.Column("includes_memory", sa.Boolean(), nullable=False),
        sa.Column("automatic", sa.Boolean(), nullable=False),
        sa.Column("metadata_redacted", JSON_TYPE, nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint(
            "status IN ('creating','ready','restoring','failed','deleted')",
            name="ck_computer_snapshot_status",
        ),
        sa.ForeignKeyConstraint(
            ["workspace_id", "computer_id"], ["computers.workspace_id", "computers.id"],
            name="fk_computer_snapshot_computer", ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["workspace_id", "created_by_user_id"],
            ["workspace_memberships.workspace_id", "workspace_memberships.user_id"],
            name="fk_computer_snapshot_creator",
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("workspace_id", "id", name="uq_computer_snapshot_workspace_id"),
        sa.UniqueConstraint(
            "workspace_id", "provider_snapshot_id", name="uq_computer_provider_snapshot"
        ),
    )
    op.create_index(op.f("ix_computer_snapshots_workspace_id"), "computer_snapshots",
                    ["workspace_id"])
    op.create_index(op.f("ix_computer_snapshots_computer_id"), "computer_snapshots",
                    ["computer_id"])
    op.create_table(
        "computer_metrics",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("workspace_id", sa.Uuid(), nullable=False),
        sa.Column("computer_id", sa.Uuid(), nullable=False),
        sa.Column("cpu_percent", sa.Integer(), nullable=False),
        sa.Column("memory_used_mb", sa.Integer(), nullable=False),
        sa.Column("disk_used_gb", sa.Integer(), nullable=False),
        sa.Column("network_rx_bytes", sa.BigInteger(), nullable=False),
        sa.Column("network_tx_bytes", sa.BigInteger(), nullable=False),
        sa.Column("top_processes", JSON_TYPE, nullable=False),
        sa.Column("recorded_at", sa.DateTime(timezone=True), server_default=sa.func.now(),
                  nullable=False),
        sa.CheckConstraint("cpu_percent BETWEEN 0 AND 100", name="ck_computer_metric_cpu"),
        sa.CheckConstraint("disk_used_gb >= 0", name="ck_computer_metric_disk"),
        sa.CheckConstraint("memory_used_mb >= 0", name="ck_computer_metric_memory"),
        sa.ForeignKeyConstraint(
            ["workspace_id", "computer_id"], ["computers.workspace_id", "computers.id"],
            name="fk_computer_metric_computer", ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(op.f("ix_computer_metrics_workspace_id"), "computer_metrics",
                    ["workspace_id"])
    op.create_index(op.f("ix_computer_metrics_computer_id"), "computer_metrics",
                    ["computer_id"])
    op.create_index(
        "ix_computer_metric_computer_recorded", "computer_metrics",
        ["computer_id", "recorded_at"],
    )


def _seed_and_backfill() -> None:
    connection = op.get_bind()
    workspaces = sa.table("workspaces", sa.column("id", sa.Uuid()))
    memberships = sa.table(
        "workspace_memberships", sa.column("workspace_id", sa.Uuid()),
        sa.column("user_id", sa.Uuid()), sa.column("status", sa.String()),
    )
    agents = sa.table(
        "agents", sa.column("id", sa.Uuid()), sa.column("workspace_id", sa.Uuid()),
        sa.column("created_by_user_id", sa.Uuid()), sa.column("name", sa.String()),
        sa.column("status", sa.String()), sa.column("autonomy_level", sa.Integer()),
        sa.column("computer_required", sa.Boolean()), sa.column("computer_id", sa.Uuid()),
        sa.column("system_privilege", sa.String()), sa.column("computer_autonomy", sa.Integer()),
        sa.column("computer_network_policy", JSON_TYPE), sa.column("computer_file_policy", JSON_TYPE),
    )
    templates = sa.table(
        "computer_templates", sa.column("id", sa.Uuid()), sa.column("workspace_id", sa.Uuid()),
        sa.column("template_key", sa.String()), sa.column("name", sa.String()),
        sa.column("os_family", sa.String()), sa.column("os_distribution", sa.String()),
        sa.column("os_version", sa.String()), sa.column("browser", sa.String()),
        sa.column("minimum_cpu", sa.Integer()), sa.column("minimum_memory_mb", sa.Integer()),
        sa.column("minimum_disk_gb", sa.Integer()),
        sa.column("windows_license_required", sa.Boolean()), sa.column("status", sa.String()),
    )
    computers = sa.table(
        "computers", sa.column("id", sa.Uuid()), sa.column("workspace_id", sa.Uuid()),
        sa.column("template_id", sa.Uuid()), sa.column("created_by_user_id", sa.Uuid()),
        sa.column("name", sa.String()), sa.column("os_family", sa.String()),
        sa.column("os_distribution", sa.String()), sa.column("os_version", sa.String()),
        sa.column("compute_provider", sa.String()), sa.column("cpu_cores", sa.Integer()),
        sa.column("memory_mb", sa.Integer()), sa.column("disk_gb", sa.Integer()),
        sa.column("browser", sa.String()), sa.column("persistent_disk", sa.Boolean()),
        sa.column("system_privilege", sa.String()), sa.column("start_policy", sa.String()),
        sa.column("status", sa.String()), sa.column("desired_state", sa.String()),
        sa.column("version", sa.Integer()),
    )
    profiles = sa.table(
        "computer_profiles", sa.column("id", sa.Uuid()), sa.column("workspace_id", sa.Uuid()),
        sa.column("created_by_user_id", sa.Uuid()), sa.column("profile_key", sa.String()),
        sa.column("name", sa.String()), sa.column("storage_key", sa.String()),
        sa.column("status", sa.String()), sa.column("retention_days", sa.Integer()),
        sa.column("version", sa.Integer()), sa.column("revoked_at", sa.DateTime(timezone=True)),
        sa.column("agent_id", sa.Uuid()), sa.column("computer_id", sa.Uuid()),
    )
    sessions = sa.table(
        "computer_sessions", sa.column("workspace_id", sa.Uuid()),
        sa.column("profile_id", sa.Uuid()), sa.column("status", sa.String()),
        sa.column("control_owner", sa.String()), sa.column("worker_id", sa.String()),
        sa.column("failure_code", sa.String()), sa.column("failure_message", sa.String()),
        sa.column("stopped_at", sa.DateTime(timezone=True)), sa.column("version", sa.Integer()),
    )
    policies = sa.table(
        "computer_network_policies", sa.column("id", sa.Uuid()),
        sa.column("workspace_id", sa.Uuid()), sa.column("computer_id", sa.Uuid()),
        sa.column("internet_access", sa.Boolean()), sa.column("lan_access", sa.Boolean()),
        sa.column("platform_api_access", sa.Boolean()),
        sa.column("other_agent_networks", sa.Boolean()),
        sa.column("host_management_access", sa.Boolean()), sa.column("allowed_domains", JSON_TYPE),
        sa.column("blocked_domains", JSON_TYPE), sa.column("version", sa.Integer()),
    )
    limits = sa.table(
        "workspace_computer_limits", sa.column("workspace_id", sa.Uuid()),
        sa.column("maximum_computers", sa.Integer()),
        sa.column("maximum_running_computers", sa.Integer()),
        sa.column("maximum_cpu_per_computer", sa.Integer()),
        sa.column("maximum_memory_mb", sa.Integer()), sa.column("maximum_disk_gb", sa.Integer()),
    )
    for (workspace_id,) in connection.execute(sa.select(workspaces.c.id)).all():
        template_ids: dict[str, uuid.UUID] = {}
        for item in TEMPLATES:
            template_id = uuid.uuid4()
            template_ids[item[0]] = template_id
            connection.execute(templates.insert().values(
                id=template_id, workspace_id=workspace_id, template_key=item[0], name=item[1],
                os_family=item[2], os_distribution=item[3], os_version=item[4], browser=item[5],
                minimum_cpu=item[6], minimum_memory_mb=item[7], minimum_disk_gb=item[8],
                windows_license_required=item[9], status="unconfigured",
            ))
        connection.execute(limits.insert().values(
            workspace_id=workspace_id, maximum_computers=10,
            maximum_running_computers=4, maximum_cpu_per_computer=8,
            maximum_memory_mb=16384, maximum_disk_gb=200,
        ))
        workspace_agents = connection.execute(sa.select(
            agents.c.id, agents.c.created_by_user_id, agents.c.name, agents.c.status,
            agents.c.autonomy_level,
        ).where(agents.c.workspace_id == workspace_id)).all()
        fallback_creator = connection.execute(sa.select(memberships.c.user_id).where(
            memberships.c.workspace_id == workspace_id,
            memberships.c.status == "active",
        ).limit(1)).scalar_one_or_none()
        unassigned_profiles = list(connection.execute(sa.select(profiles.c.id).where(
            profiles.c.workspace_id == workspace_id,
            profiles.c.agent_id.is_(None),
        )).scalars())
        if unassigned_profiles:
            connection.execute(sessions.update().where(
                sessions.c.workspace_id == workspace_id,
                sessions.c.profile_id.in_(unassigned_profiles),
                sessions.c.status.not_in(("stopped", "failed")),
            ).values(
                status="stopped", control_owner="none", worker_id=None,
                failure_code="COMPUTER_PROFILE_MIGRATED",
                failure_message=(
                    "The ambiguous legacy browser profile was revoked during dedicated-computer "
                    "migration."
                ),
                stopped_at=sa.func.now(), version=sessions.c.version + 1,
            ))
            connection.execute(profiles.update().where(
                profiles.c.workspace_id == workspace_id,
                profiles.c.id.in_(unassigned_profiles),
            ).values(status="revoked", revoked_at=sa.func.now()))
        for agent_id, creator_id, agent_name, _agent_status, autonomy in workspace_agents:
            computer_id = uuid.uuid4()
            creator_id = creator_id or fallback_creator
            connection.execute(computers.insert().values(
                id=computer_id, workspace_id=workspace_id,
                template_id=template_ids["ubuntu-desktop-24-04"],
                created_by_user_id=creator_id,
                name=f"{agent_name} - Private Computer"[:160], os_family="linux",
                os_distribution="Ubuntu Desktop", os_version="24.04 LTS",
                compute_provider="unconfigured", cpu_cores=2, memory_mb=4096, disk_gb=40,
                browser="chromium", persistent_disk=True, system_privilege="standard_user",
                start_policy="start_when_needed", status="unconfigured", desired_state="stopped",
                version=1,
            ))
            network = {
                "internet_access": True, "lan_access": False, "platform_api_access": True,
                "other_agent_networks": False, "allowed_domains": [], "blocked_domains": [],
            }
            connection.execute(policies.insert().values(
                id=uuid.uuid4(), workspace_id=workspace_id, computer_id=computer_id,
                internet_access=True, lan_access=False, platform_api_access=True,
                other_agent_networks=False, host_management_access=False,
                allowed_domains=[], blocked_domains=[], version=1,
            ))
            connection.execute(agents.update().where(
                agents.c.workspace_id == workspace_id, agents.c.id == agent_id,
            ).values(
                computer_required=True, computer_id=computer_id,
                system_privilege="standard_user", computer_autonomy=autonomy,
                computer_network_policy=network,
                computer_file_policy={"scope": "guest_only", "host_access": False},
                status="disabled",
            ))
            connection.execute(profiles.insert().values(
                id=uuid.uuid4(), workspace_id=workspace_id, agent_id=agent_id,
                computer_id=computer_id, created_by_user_id=creator_id,
                profile_key=f"agent-{agent_id.hex}",
                name=f"{agent_name} - Private Computer"[:160], storage_key=uuid.uuid4().hex,
                status="disabled", retention_days=30, version=1,
            ))


def downgrade() -> None:
    op.drop_index("uq_computer_active_session_profile", table_name="computer_sessions")
    with op.batch_alter_table("computer_profiles") as batch:
        batch.drop_constraint("ck_computer_profile_computer_owner", type_="check")
        batch.drop_constraint("ck_computer_profile_owner", type_="check")
        batch.drop_constraint("uq_computer_profile_computer", type_="unique")
        batch.drop_constraint("uq_computer_profile_agent", type_="unique")
        batch.drop_constraint("fk_computer_profile_computer", type_="foreignkey")
        batch.drop_constraint("fk_computer_profile_agent", type_="foreignkey")
    op.drop_index(op.f("ix_computer_profiles_computer_id"), table_name="computer_profiles")
    op.drop_index(op.f("ix_computer_profiles_agent_id"), table_name="computer_profiles")
    with op.batch_alter_table("computer_profiles") as batch:
        batch.drop_column("computer_id")
        batch.drop_column("agent_id")
    with op.batch_alter_table("agents") as batch:
        batch.drop_constraint("ck_agent_required_computer", type_="check")
        batch.drop_constraint("ck_active_agent_has_computer", type_="check")
        batch.drop_constraint("ck_agent_system_privilege", type_="check")
        batch.drop_constraint("ck_agent_computer_autonomy", type_="check")
        batch.drop_constraint("uq_agent_dedicated_computer", type_="unique")
        batch.drop_constraint("fk_agent_computer", type_="foreignkey")
    op.drop_index(op.f("ix_agents_computer_id"), table_name="agents")
    with op.batch_alter_table("agents") as batch:
        batch.drop_column("computer_file_policy")
        batch.drop_column("computer_network_policy")
        batch.drop_column("computer_autonomy")
        batch.drop_column("system_privilege")
        batch.drop_column("computer_id")
        batch.drop_column("computer_required")
    op.drop_index("ix_computer_metric_computer_recorded", table_name="computer_metrics")
    op.drop_index(op.f("ix_computer_metrics_computer_id"), table_name="computer_metrics")
    op.drop_index(op.f("ix_computer_metrics_workspace_id"), table_name="computer_metrics")
    op.drop_table("computer_metrics")
    op.drop_index(op.f("ix_computer_snapshots_computer_id"), table_name="computer_snapshots")
    op.drop_index(op.f("ix_computer_snapshots_workspace_id"), table_name="computer_snapshots")
    op.drop_table("computer_snapshots")
    op.drop_index(op.f("ix_computer_operations_computer_id"), table_name="computer_operations")
    op.drop_index(op.f("ix_computer_operations_workspace_id"), table_name="computer_operations")
    op.drop_table("computer_operations")
    op.drop_table("workspace_computer_limits")
    op.drop_index(
        op.f("ix_computer_network_policies_computer_id"),
        table_name="computer_network_policies",
    )
    op.drop_index(
        op.f("ix_computer_network_policies_workspace_id"),
        table_name="computer_network_policies",
    )
    op.drop_table("computer_network_policies")
    op.drop_index(op.f("ix_computers_workspace_id"), table_name="computers")
    op.drop_table("computers")
    op.drop_index(op.f("ix_computer_templates_workspace_id"), table_name="computer_templates")
    op.drop_table("computer_templates")
