/**
 * POST /api/admin/migrate
 *
 * One-shot migration endpoint.  Requires the ADMIN_MIGRATE_SECRET env var
 * to be set and passed as x-admin-secret header (or ?secret= query param).
 *
 * Idempotent — safe to call multiple times (all statements use IF NOT EXISTS).
 *
 * curl -X POST https://vibnai.com/api/admin/migrate \
 *   -H "x-admin-secret: <ADMIN_MIGRATE_SECRET>"
 */
import { NextRequest, NextResponse } from "next/server";
import { query } from "@/lib/db-postgres";
import { readFileSync } from "fs";
import { join } from "path";

export async function POST(req: NextRequest) {
  const secret = process.env.ADMIN_MIGRATE_SECRET ?? "";
  if (!secret) {
    return NextResponse.json(
      { error: "ADMIN_MIGRATE_SECRET env var not set — migration endpoint disabled" },
      { status: 403 }
    );
  }

  const incoming =
    req.headers.get("x-admin-secret") ??
    new URL(req.url).searchParams.get("secret") ??
    "";

  if (incoming !== secret) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const results: Array<{ statement: string; ok: boolean; error?: string }> = [];

  // Inline the DDL so this works even if the SQL file isn't on the runtime fs
  const statements = [
    `CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`,

    `CREATE TABLE IF NOT EXISTS fs_users (
      id         TEXT PRIMARY KEY,
      user_id    TEXT,
      data       JSONB NOT NULL DEFAULT '{}',
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS fs_users_email_idx ON fs_users ((data->>'email'))`,
    `CREATE INDEX IF NOT EXISTS fs_users_user_id_idx ON fs_users (user_id)`,

    `CREATE TABLE IF NOT EXISTS fs_projects (
      id         TEXT PRIMARY KEY,
      user_id    TEXT NOT NULL,
      workspace  TEXT NOT NULL,
      slug       TEXT NOT NULL UNIQUE,
      data       JSONB NOT NULL DEFAULT '{}',
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS fs_projects_user_idx ON fs_projects (user_id)`,
    `CREATE INDEX IF NOT EXISTS fs_projects_workspace_idx ON fs_projects (workspace)`,

    `CREATE TABLE IF NOT EXISTS fs_sessions (
      id         TEXT PRIMARY KEY,
      user_id    TEXT,
      data       JSONB NOT NULL DEFAULT '{}',
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS fs_sessions_user_idx ON fs_sessions (user_id)`,
    `CREATE INDEX IF NOT EXISTS fs_sessions_project_idx ON fs_sessions ((data->>'projectId'))`,

    // agent_sessions uses TEXT for project_id to match fs_projects.id
    `CREATE TABLE IF NOT EXISTS agent_sessions (
      id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      project_id    TEXT NOT NULL,
      app_name      TEXT NOT NULL,
      app_path      TEXT NOT NULL,
      task          TEXT NOT NULL,
      plan          JSONB,
      status        TEXT NOT NULL DEFAULT 'pending',
      output        JSONB NOT NULL DEFAULT '[]',
      changed_files JSONB NOT NULL DEFAULT '[]',
      error         TEXT,
      started_at    TIMESTAMPTZ,
      completed_at  TIMESTAMPTZ,
      created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS agent_sessions_project_idx ON agent_sessions (project_id, created_at DESC)`,
    `CREATE INDEX IF NOT EXISTS agent_sessions_status_idx ON agent_sessions (status)`,

    `CREATE TABLE IF NOT EXISTS agent_session_events (
      id BIGSERIAL PRIMARY KEY,
      session_id UUID NOT NULL REFERENCES agent_sessions(id) ON DELETE CASCADE,
      project_id TEXT NOT NULL,
      seq INT NOT NULL,
      ts TIMESTAMPTZ NOT NULL,
      type TEXT NOT NULL,
      payload JSONB NOT NULL DEFAULT '{}'::jsonb,
      client_event_id UUID UNIQUE,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
      UNIQUE(session_id, seq)
    )`,
    `CREATE INDEX IF NOT EXISTS agent_session_events_session_seq_idx ON agent_session_events (session_id, seq)`,

    // NextAuth / Prisma tables
    `CREATE TABLE IF NOT EXISTS users (
      id             TEXT PRIMARY KEY,
      name           TEXT,
      email          TEXT UNIQUE,
      email_verified TIMESTAMPTZ,
      image          TEXT
    )`,
    `CREATE TABLE IF NOT EXISTS accounts (
      id                  TEXT PRIMARY KEY,
      user_id             TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
      type                TEXT NOT NULL,
      provider            TEXT NOT NULL,
      provider_account_id TEXT NOT NULL,
      refresh_token       TEXT,
      access_token        TEXT,
      expires_at          INTEGER,
      token_type          TEXT,
      scope               TEXT,
      id_token            TEXT,
      session_state       TEXT,
      UNIQUE (provider, provider_account_id)
    )`,
    `CREATE TABLE IF NOT EXISTS sessions (
      id            TEXT PRIMARY KEY,
      session_token TEXT UNIQUE NOT NULL,
      user_id       TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
      expires       TIMESTAMPTZ NOT NULL
    )`,
    `CREATE TABLE IF NOT EXISTS verification_tokens (
      identifier TEXT NOT NULL,
      token      TEXT UNIQUE NOT NULL,
      expires    TIMESTAMPTZ NOT NULL,
      UNIQUE (identifier, token)
    )`,

    // ── Vibn workspaces (logical tenancy on top of Coolify) ──────────
    // One workspace per Vibn account. Holds a Coolify Project UUID
    // (the team boundary inside Coolify) and a Gitea org name.
    `CREATE TABLE IF NOT EXISTS vibn_workspaces (
      id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      slug                  TEXT NOT NULL UNIQUE,
      name                  TEXT NOT NULL,
      owner_user_id         TEXT NOT NULL,
      coolify_project_uuid  TEXT,
      coolify_team_id       INT,
      gitea_org             TEXT,
      provision_status      TEXT NOT NULL DEFAULT 'pending',
      provision_error       TEXT,
      created_at            TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at            TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_workspaces_owner_idx ON vibn_workspaces (owner_user_id)`,

    `CREATE TABLE IF NOT EXISTS vibn_workspace_members (
      id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      workspace_id UUID NOT NULL REFERENCES vibn_workspaces(id) ON DELETE CASCADE,
      user_id      TEXT NOT NULL,
      role         TEXT NOT NULL DEFAULT 'member',
      created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
      UNIQUE (workspace_id, user_id)
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_workspace_members_user_idx ON vibn_workspace_members (user_id)`,

    `CREATE TABLE IF NOT EXISTS vibn_workspace_api_keys (
      id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      workspace_id UUID NOT NULL REFERENCES vibn_workspaces(id) ON DELETE CASCADE,
      name         TEXT NOT NULL,
      key_prefix   TEXT NOT NULL,
      key_hash     TEXT NOT NULL UNIQUE,
      scopes       JSONB NOT NULL DEFAULT '["workspace:*"]'::jsonb,
      created_by   TEXT NOT NULL,
      last_used_at TIMESTAMPTZ,
      revoked_at   TIMESTAMPTZ,
      created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_workspace_api_keys_workspace_idx ON vibn_workspace_api_keys (workspace_id)`,

    // Tag projects with the workspace they belong to (nullable until backfill).
    // The pre-existing fs_projects.workspace TEXT column stays for the legacy slug;
    // this new UUID FK is the canonical link.
    `ALTER TABLE fs_projects ADD COLUMN IF NOT EXISTS vibn_workspace_id UUID REFERENCES vibn_workspaces(id) ON DELETE SET NULL`,
    `CREATE INDEX IF NOT EXISTS fs_projects_vibn_workspace_idx ON fs_projects (vibn_workspace_id)`,

    // ── Per-workspace Gitea bot user (for direct AI access) ──────────
    // Each workspace gets its own Gitea user with a PAT scoped to the
    // workspace's org, so AI agents can `git clone` / push directly
    // without ever touching the root admin token.
    //
    // Token is encrypted at rest with AES-256-GCM using VIBN_SECRETS_KEY.
    // Layout: iv(12) || ciphertext || authTag(16), base64-encoded.
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS gitea_bot_username TEXT`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS gitea_bot_user_id INT`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS gitea_bot_token_encrypted TEXT`,

    // ── Phase 4: workspace-owned deploy infra ────────────────────────
    // Lets AI agents create Coolify applications/databases/services
    // against a Gitea repo the bot can read, routed to the right
    // server and Docker destination, and exposed under the workspace's
    // own subdomain namespace.
    //
    //   coolify_server_uuid       — which Coolify server the workspace deploys to
    //   coolify_destination_uuid  — Docker network / destination on that server
    //   coolify_environment_name  — Coolify environment (default "production")
    //   coolify_private_key_uuid  — workspace-wide SSH deploy key (Coolify-side UUID)
    //   gitea_bot_ssh_key_id      — Gitea key id for the matching public key (for rotation)
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS coolify_server_uuid TEXT`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS coolify_destination_uuid TEXT`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS coolify_environment_name TEXT NOT NULL DEFAULT 'production'`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS coolify_private_key_uuid TEXT`,
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS gitea_bot_ssh_key_id INT`,

    // ── Phase 5.1: domains (OpenSRS) + DNS + billing ledger ──────────
    //
    //   vibn_domains        — owned domains + their registration lifecycle
    //   vibn_domain_events  — audit trail (register, attach, renew, expire)
    //   vibn_billing_ledger — money in/out at the workspace level
    //
    // Reg credentials for a domain (OpenSRS manage-user password) are
    // encrypted at rest with AES-256-GCM using VIBN_SECRETS_KEY.
    //
    // Workspace residency preference for DNS:
    //   dns_provider = 'cloud_dns'  (default, public records)
    //   dns_provider = 'cira_dzone' (strict Canadian residency, future)
    `ALTER TABLE vibn_workspaces ADD COLUMN IF NOT EXISTS dns_provider TEXT NOT NULL DEFAULT 'cloud_dns'`,

    `CREATE TABLE IF NOT EXISTS vibn_domains (
      id                         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      workspace_id               UUID NOT NULL REFERENCES vibn_workspaces(id) ON DELETE CASCADE,
      domain                     TEXT NOT NULL,
      tld                        TEXT NOT NULL,
      status                     TEXT NOT NULL DEFAULT 'pending',
      registrar                  TEXT NOT NULL DEFAULT 'opensrs',
      registrar_order_id         TEXT,
      registrar_username         TEXT,
      registrar_password_enc     TEXT,
      period_years               INT NOT NULL DEFAULT 1,
      whois_privacy              BOOLEAN NOT NULL DEFAULT TRUE,
      auto_renew                 BOOLEAN NOT NULL DEFAULT FALSE,
      registered_at              TIMESTAMPTZ,
      expires_at                 TIMESTAMPTZ,
      dns_provider               TEXT,
      dns_zone_id                TEXT,
      dns_nameservers            JSONB,
      last_reconciled_at         TIMESTAMPTZ,
      price_paid_cents           INT,
      price_currency             TEXT,
      created_by                 TEXT NOT NULL,
      created_at                 TIMESTAMPTZ NOT NULL DEFAULT now(),
      updated_at                 TIMESTAMPTZ NOT NULL DEFAULT now(),
      UNIQUE (domain)
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_domains_workspace_idx ON vibn_domains (workspace_id)`,
    `CREATE INDEX IF NOT EXISTS vibn_domains_status_idx ON vibn_domains (status)`,
    `CREATE INDEX IF NOT EXISTS vibn_domains_expires_idx ON vibn_domains (expires_at) WHERE expires_at IS NOT NULL`,

    `CREATE TABLE IF NOT EXISTS vibn_domain_events (
      id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      domain_id    UUID NOT NULL REFERENCES vibn_domains(id) ON DELETE CASCADE,
      workspace_id UUID NOT NULL REFERENCES vibn_workspaces(id) ON DELETE CASCADE,
      type         TEXT NOT NULL,
      payload      JSONB NOT NULL DEFAULT '{}'::jsonb,
      created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_domain_events_domain_idx ON vibn_domain_events (domain_id, created_at DESC)`,

    `CREATE TABLE IF NOT EXISTS vibn_billing_ledger (
      id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
      workspace_id UUID NOT NULL REFERENCES vibn_workspaces(id) ON DELETE CASCADE,
      kind         TEXT NOT NULL,
      amount_cents INT NOT NULL,
      currency     TEXT NOT NULL DEFAULT 'CAD',
      ref_type     TEXT,
      ref_id       TEXT,
      note         TEXT,
      created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
    )`,
    `CREATE INDEX IF NOT EXISTS vibn_billing_ledger_workspace_idx ON vibn_billing_ledger (workspace_id, created_at DESC)`,
    `CREATE INDEX IF NOT EXISTS vibn_billing_ledger_ref_idx ON vibn_billing_ledger (ref_type, ref_id)`,
  ];

  for (const stmt of statements) {
    const label = stmt.trim().split("\n")[0].trim().slice(0, 80);
    try {
      await query(stmt, []);
      results.push({ statement: label, ok: true });
    } catch (err) {
      results.push({
        statement: label,
        ok: false,
        error: err instanceof Error ? err.message : String(err),
      });
    }
  }

  const failed = results.filter(r => !r.ok);
  return NextResponse.json(
    { ok: failed.length === 0, results },
    { status: failed.length === 0 ? 200 : 207 }
  );
}
