use sqlx::{PgPool, query};

pub async fn apply_postgres(pool: &PgPool) -> Result<(), sqlx::Error> {
    query(
        "create table if not exists workspaces (
            id text primary key,
            slug text not null unique,
            display_name text not null,
            status text not null,
            settings_json jsonb not null default '{}'::jsonb,
            created_at timestamptz not null,
            updated_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists users (
            id text primary key,
            email text not null unique,
            display_name text not null,
            password_hash text null,
            status text not null,
            created_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query("alter table users add column if not exists password_hash text null")
        .execute(pool)
        .await?;

    query(
        "insert into users (
            id,
            email,
            display_name,
            status,
            created_at
        ) values (
            'user_default_owner',
            'owner@crank.local',
            'Workspace Owner',
            'active',
            now()
        )
        on conflict (id) do nothing",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists memberships (
            workspace_id text not null references workspaces(id) on delete cascade,
            user_id text not null references users(id) on delete cascade,
            role text not null,
            created_at timestamptz not null,
            primary key (workspace_id, user_id)
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists user_sessions (
            id text primary key,
            user_id text not null references users(id) on delete cascade,
            current_workspace_id text null references workspaces(id) on delete set null,
            secret_hash text not null,
            status text not null,
            expires_at timestamptz not null,
            last_seen_at timestamptz null,
            created_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "alter table user_sessions
         add column if not exists current_workspace_id text null references workspaces(id) on delete set null",
    )
    .execute(pool)
    .await?;

    query(
        "insert into workspaces (
            id,
            slug,
            display_name,
            status,
            settings_json,
            created_at,
            updated_at
        ) values (
            'ws_default',
            'default',
            'Default Workspace',
            'active',
            '{}'::jsonb,
            now(),
            now()
        )
        on conflict (id) do nothing",
    )
    .execute(pool)
    .await?;

    query(
        "insert into memberships (
            workspace_id,
            user_id,
            role,
            created_at
        ) values (
            'ws_default',
            'user_default_owner',
            'owner',
            now()
        )
        on conflict (workspace_id, user_id) do nothing",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists invitation_tokens (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            email text not null,
            role text not null,
            status text not null,
            token_hash text not null,
            expires_at timestamptz not null,
            created_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists platform_api_keys (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            agent_id text null,
            name text not null,
            prefix text not null,
            secret_hash text not null,
            scopes_json jsonb not null,
            status text not null,
            created_at timestamptz not null,
            last_used_at timestamptz null,
            revoked_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create unique index if not exists platform_api_keys_workspace_name_idx on platform_api_keys(workspace_id, name)",
    )
    .execute(pool)
    .await?;
    query("alter table platform_api_keys add column if not exists agent_id text null")
        .execute(pool)
        .await?;

    query(
        "insert into workspaces (
            id,
            slug,
            display_name,
            status,
            settings_json,
            created_at,
            updated_at
        ) values (
            'ws_default',
            'default',
            'Default Workspace',
            'active',
            '{}'::jsonb,
            now(),
            now()
        )
        on conflict (id) do nothing",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists operations (
            id text primary key,
            workspace_id text null references workspaces(id) on delete cascade,
            name text not null,
            display_name text not null,
            category text not null default 'general',
            protocol text not null,
            security_level text not null default 'standard',
            status text not null,
            current_draft_version integer not null default 1,
            latest_published_version integer null,
            created_at timestamptz not null,
            updated_at timestamptz not null,
            published_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query("alter table operations add column if not exists workspace_id text null references workspaces(id) on delete cascade")
        .execute(pool)
        .await?;
    query(
        "alter table operations add column if not exists category text not null default 'general'",
    )
    .execute(pool)
    .await?;
    query(
        "alter table operations add column if not exists security_level text not null default 'standard'",
    )
    .execute(pool)
    .await?;
    query("update operations set workspace_id = 'ws_default' where workspace_id is null")
        .execute(pool)
        .await?;
    query("alter table operations alter column workspace_id set not null")
        .execute(pool)
        .await?;
    query("alter table operations drop constraint if exists operations_name_key")
        .execute(pool)
        .await?;
    query(
        "create unique index if not exists operations_workspace_name_idx on operations(workspace_id, name)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists operation_versions (
            operation_id text not null references operations(id) on delete cascade,
            version integer not null,
            status text not null,
            target_json jsonb not null,
            input_schema_json jsonb not null,
            output_schema_json jsonb not null,
            input_mapping_json jsonb not null,
            output_mapping_json jsonb not null,
            execution_config_json jsonb not null,
            tool_description_json jsonb not null,
            samples_json jsonb null,
            generated_draft_json jsonb null,
            config_export_json jsonb null,
            change_note text null,
            created_at timestamptz not null,
            created_by text null,
            primary key (operation_id, version)
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists published_operations (
            operation_id text primary key references operations(id) on delete cascade,
            version integer not null,
            published_at timestamptz not null,
            published_by text null,
            foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists operation_samples (
            id text primary key,
            operation_id text not null references operations(id) on delete cascade,
            version integer not null,
            sample_kind text not null,
            storage_ref text not null,
            content_type text not null,
            file_name text null,
            created_at timestamptz not null,
            foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists descriptors (
            id text primary key,
            operation_id text null references operations(id) on delete cascade,
            version integer null,
            descriptor_kind text not null,
            storage_ref text not null,
            source_name text null,
            package_index_json jsonb null,
            created_at timestamptz not null,
            foreign key (operation_id, version) references operation_versions(operation_id, version) on delete cascade
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists auth_profiles (
            id text primary key,
            workspace_id text null references workspaces(id) on delete cascade,
            name text not null,
            kind text not null,
            config_json jsonb not null,
            created_at timestamptz not null,
            updated_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query("alter table auth_profiles add column if not exists workspace_id text null references workspaces(id) on delete cascade")
        .execute(pool)
        .await?;
    query("update auth_profiles set workspace_id = 'ws_default' where workspace_id is null")
        .execute(pool)
        .await?;
    query("alter table auth_profiles alter column workspace_id set not null")
        .execute(pool)
        .await?;
    query("alter table auth_profiles drop constraint if exists auth_profiles_name_key")
        .execute(pool)
        .await?;
    query(
        "create unique index if not exists auth_profiles_workspace_name_idx on auth_profiles(workspace_id, name)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists secrets (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            name text not null,
            kind text not null,
            status text not null,
            current_version integer not null,
            last_used_at timestamptz null,
            created_at timestamptz not null,
            updated_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create unique index if not exists secrets_workspace_name_idx on secrets(workspace_id, name)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists secret_versions (
            secret_id text not null references secrets(id) on delete cascade,
            version integer not null,
            ciphertext text not null,
            key_version text not null,
            created_at timestamptz not null,
            created_by text null references users(id) on delete set null,
            primary key (secret_id, version)
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists yaml_import_jobs (
            id text primary key,
            source_sample_id text null references operation_samples(id) on delete set null,
            status text not null,
            format_version text not null,
            mode text not null,
            result_operation_id text null references operations(id) on delete set null,
            result_version integer null,
            error_text text null,
            created_at timestamptz not null,
            finished_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists agents (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            slug text not null,
            display_name text not null,
            description text not null,
            status text not null,
            current_draft_version integer not null default 1,
            latest_published_version integer null,
            created_at timestamptz not null,
            updated_at timestamptz not null,
            published_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create unique index if not exists agents_workspace_slug_idx on agents(workspace_id, slug)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists agent_versions (
            agent_id text not null references agents(id) on delete cascade,
            version integer not null,
            status text not null,
            instructions_json jsonb not null,
            tool_selection_policy_json jsonb not null,
            created_at timestamptz not null,
            primary key (agent_id, version)
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists agent_operation_bindings (
            agent_id text not null references agents(id) on delete cascade,
            agent_version integer not null,
            operation_id text not null references operations(id) on delete cascade,
            operation_version integer not null,
            tool_name text not null,
            tool_title text not null,
            tool_description_override text null,
            enabled boolean not null default true,
            foreign key (agent_id, agent_version) references agent_versions(agent_id, version) on delete cascade,
            foreign key (operation_id, operation_version) references operation_versions(operation_id, version) on delete cascade
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create unique index if not exists agent_bindings_tool_name_idx on agent_operation_bindings(agent_id, agent_version, tool_name)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists published_agents (
            agent_id text primary key references agents(id) on delete cascade,
            version integer not null,
            published_at timestamptz not null,
            published_by text null,
            foreign key (agent_id, version) references agent_versions(agent_id, version) on delete cascade
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists invocation_logs (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            agent_id text null references agents(id) on delete set null,
            operation_id text not null references operations(id) on delete cascade,
            source text not null,
            level text not null,
            status text not null,
            tool_name text not null,
            message text not null,
            request_id text null,
            status_code integer null,
            duration_ms bigint not null,
            error_kind text null,
            request_preview_json jsonb not null,
            response_preview_json jsonb not null,
            created_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists invocation_logs_workspace_created_idx on invocation_logs(workspace_id, created_at desc)",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists invocation_logs_workspace_operation_created_idx on invocation_logs(workspace_id, operation_id, created_at desc)",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists invocation_logs_workspace_agent_created_idx on invocation_logs(workspace_id, agent_id, created_at desc)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists usage_rollups (
            workspace_id text not null references workspaces(id) on delete cascade,
            agent_id text null references agents(id) on delete cascade,
            operation_id text null references operations(id) on delete cascade,
            period text not null,
            calls_total bigint not null,
            calls_ok bigint not null,
            calls_error bigint not null,
            p50_ms bigint not null,
            p95_ms bigint not null,
            p99_ms bigint not null,
            updated_at timestamptz not null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists stream_sessions (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            agent_id text null references agents(id) on delete set null,
            operation_id text not null references operations(id) on delete cascade,
            protocol text not null,
            mode text not null,
            status text not null,
            cursor_json jsonb null,
            state_json jsonb not null,
            expires_at timestamptz not null,
            last_poll_at timestamptz null,
            created_at timestamptz not null,
            closed_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists stream_sessions_workspace_status_idx
         on stream_sessions(workspace_id, status, created_at desc)",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists stream_sessions_expires_at_idx
         on stream_sessions(expires_at)",
    )
    .execute(pool)
    .await?;

    query(
        "create table if not exists async_jobs (
            id text primary key,
            workspace_id text not null references workspaces(id) on delete cascade,
            agent_id text null references agents(id) on delete set null,
            operation_id text not null references operations(id) on delete cascade,
            status text not null,
            progress_json jsonb not null,
            result_json jsonb null,
            error_json jsonb null,
            expires_at timestamptz null,
            last_poll_at timestamptz null,
            created_at timestamptz not null,
            updated_at timestamptz not null,
            finished_at timestamptz null
        )",
    )
    .execute(pool)
    .await?;

    query("alter table async_jobs add column if not exists last_poll_at timestamptz null")
        .execute(pool)
        .await?;

    query(
        "create index if not exists async_jobs_workspace_status_idx
         on async_jobs(workspace_id, status, updated_at desc)",
    )
    .execute(pool)
    .await?;

    query(
        "create index if not exists async_jobs_expires_at_idx
         on async_jobs(expires_at)",
    )
    .execute(pool)
    .await?;

    Ok(())
}
