/**
 * Agent Sessions API
 *
 * POST /api/projects/[projectId]/agent/sessions
 *   Create a new agent session and kick it off via vibn-agent-runner.
 *   Body: { appName, appPath, task }
 *
 * GET /api/projects/[projectId]/agent/sessions
 *   List all sessions for a project, newest first.
 */
import { NextResponse } from "next/server";
import { requireWorkspacePrincipal } from "@/lib/auth/workspace-auth";
import { query, queryOne } from "@/lib/db-postgres";
import {
  listWorkspaceApiKeys,
  mintWorkspaceApiKey,
  revealWorkspaceApiKey,
} from "@/lib/auth/workspace-auth";

const AGENT_RUNNER_URL =
  process.env.AGENT_RUNNER_URL ?? "http://localhost:3333";

// Verify the agent_sessions table is reachable. If it doesn't exist yet,
// throw a descriptive error instead of a generic "Failed to create session".
// Run POST /api/admin/migrate once to create the table.
async function ensureTable() {
  await query(`SELECT 1 FROM agent_sessions LIMIT 0`, []);
}

// ── POST — create session ────────────────────────────────────────────────────

export async function POST(
  req: Request,
  { params }: { params: Promise<{ projectId: string }> },
) {
  try {
    const { projectId } = await params;

    // 1. Authenticate the Workspace API key or Browser Session
    const principal = await requireWorkspacePrincipal(req);
    if (principal instanceof NextResponse) return principal;

    // 2. Fetch user details from principal.userId
    const userRow = await queryOne<{ id: string; data: any }>(
      `SELECT id, data FROM fs_users WHERE id = $1 LIMIT 1`,
      [principal.userId],
    );
    const email = userRow?.data?.email;
    if (!email) {
      return NextResponse.json(
        { error: "User email not found" },
        { status: 404 },
      );
    }

    const body = await req.json();
    const { appName, appPath, task } = body as {
      appName: string;
      appPath: string;
      task: string;
    };

    if (!appName || appPath === undefined || !task?.trim()) {
      return NextResponse.json(
        { error: "appName, appPath and task are required" },
        { status: 400 },
      );
    }

    await ensureTable();

    // Verify ownership and fetch giteaRepo
    const owns = await query<{ id: string; data: Record<string, unknown> }>(
      `SELECT p.id, p.data FROM fs_projects p
       JOIN fs_users u ON u.id = p.user_id
       WHERE p.id::text = $1 AND u.data->>'email' = $2 LIMIT 1`,
      [projectId, email],
    );
    if (owns.length === 0) {
      return NextResponse.json({ error: "Project not found" }, { status: 404 });
    }

    const giteaRepo = owns[0].data?.giteaRepo as string | undefined;

    // Find the Coolify UUID for this specific app so the runner can trigger a deploy
    interface AppEntry {
      name: string;
      coolifyServiceUuid?: string | null;
    }
    const apps = (owns[0].data?.apps ?? []) as AppEntry[];
    const matchedApp = apps.find((a) => a.name === appName);
    const coolifyAppUuid = matchedApp?.coolifyServiceUuid ?? undefined;

    // Create the session row
    const rows = await query<{ id: string }>(
      `INSERT INTO agent_sessions (project_id, app_name, app_path, task, status, started_at)
       VALUES ($1::uuid, $2, $3, $4, 'running', now())
       RETURNING id`,
      [projectId, appName, appPath, task.trim()],
    );
    const sessionId = rows[0].id;

    const wsResult = await query<{ workspace_id: string }>(
      `SELECT vibn_workspace_id as workspace_id FROM fs_projects WHERE id = $1 LIMIT 1`,
      [projectId],
    );
    if (!wsResult.length) {
      return NextResponse.json({ error: "Project not found" }, { status: 404 });
    }
    const workspaceId = wsResult[0].workspace_id;

    // Grab or mint a default API key for the runner to use
    let mcpToken = "";
    const keys = await listWorkspaceApiKeys(workspaceId);
    let defaultKey = keys.find(
      (k: any) => k.name === "default" && !k.revoked_at,
    );
    if (!defaultKey) {
      const minted = await mintWorkspaceApiKey({
        workspaceId,
        name: "default",
        createdBy: principal.userId,
        scopes: ["workspace:*"],
      });
      mcpToken = minted.token;
    } else {
      const revealed = await revealWorkspaceApiKey(workspaceId, defaultKey.id);
      if (revealed) mcpToken = revealed.token;
      else {
        const minted = await mintWorkspaceApiKey({
          workspaceId,
          name: "default",
          createdBy: principal.userId,
          scopes: ["workspace:*"],
        });
        mcpToken = minted.token;
      }
    }

    // Add VIBN_API_URL so the runner knows where to send MCP requests
    const vibnApiUrl =
      process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";

    // Fire-and-forget: call agent-runner to start the execution loop.
    // autoApprove: true — agent commits + deploys automatically on completion.
    fetch(`${AGENT_RUNNER_URL}/agent/execute`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        sessionId,
        projectId,
        appName,
        appPath,
        giteaRepo,
        task: task.trim(),
        autoApprove: true,
        coolifyAppUuid,
        mcpToken,
        vibnApiUrl,
      }),
    })
      .then(async (res) => {
        if (!res.ok) {
          const text = await res.text().catch(() => res.statusText);
          throw new Error(`Runner returned ${res.status}: ${text}`);
        }
      })
      .catch((err) => {
        // Agent runner may not be wired yet — log but don't fail
        console.warn("[agent] runner failed or not reachable:", err.message);
        // Mark session as failed if runner unreachable
        query(
          `UPDATE agent_sessions
         SET status = 'failed',
             error = $2,
             completed_at = now(),
             output = jsonb_build_array(jsonb_build_object(
               'ts', now()::text,
               'type', 'error',
               'text', $2
             ))
         WHERE id = $1::uuid`,
          [sessionId, `Agent runner failed: ${err.message}`],
        ).catch(() => {});
      });

    return NextResponse.json({ sessionId }, { status: 201 });
  } catch (err) {
    console.error("[agent/sessions POST]", err);
    return NextResponse.json(
      {
        error: "Failed to create session",
        details: err instanceof Error ? err.message : String(err),
      },
      { status: 500 },
    );
  }
}

// ── GET — list sessions ──────────────────────────────────────────────────────

export async function GET(
  req: Request,
  { params }: { params: Promise<{ projectId: string }> },
) {
  try {
    const { projectId } = await params;

    // 1. Authenticate the Workspace API key or Browser Session
    const principal = await requireWorkspacePrincipal(req);
    if (principal instanceof NextResponse) return principal;

    // 2. Fetch user details from principal.userId
    const userRow = await queryOne<{ id: string; data: any }>(
      `SELECT id, data FROM fs_users WHERE id = $1 LIMIT 1`,
      [principal.userId],
    );
    const email = userRow?.data?.email;
    if (!email) {
      return NextResponse.json(
        { error: "User email not found" },
        { status: 404 },
      );
    }

    await ensureTable();

    const sessions = await query<{
      id: string;
      app_name: string;
      task: string;
      status: string;
      created_at: string;
      started_at: string | null;
      completed_at: string | null;
      output: Array<{ ts: string; type: string; text: string }>;
      changed_files: Array<{ path: string; status: string }>;
      error: string | null;
    }>(
      `SELECT s.id, s.app_name, s.task, s.status,
              s.created_at, s.started_at, s.completed_at,
              s.output, s.changed_files, s.error
       FROM agent_sessions s
       JOIN fs_projects p ON p.id::text = s.project_id::text
       JOIN fs_users u ON u.id = p.user_id
       WHERE s.project_id::text = $1 AND u.data->>'email' = $2
       ORDER BY s.created_at DESC
       LIMIT 50`,
      [projectId, email],
    );

    return NextResponse.json({ sessions });
  } catch (err) {
    console.error("[agent/sessions GET]", err);
    return NextResponse.json(
      {
        error: "Failed to list sessions",
        details: err instanceof Error ? err.message : String(err),
      },
      { status: 500 },
    );
  }
}
