/**
 * GET  /api/workspaces/[slug]/apps — list Coolify apps in this workspace
 * POST /api/workspaces/[slug]/apps — create a new app from a Gitea repo
 *
 * Auth: session OR `Bearer vibn_sk_...`. The workspace's
 * `coolify_project_uuid` acts as the tenant boundary — any app whose
 * Coolify project uuid doesn't match is filtered out even if the
 * token issuer accidentally had wider reach.
 *
 * POST body:
 *   {
 *     repo:       string,              // "my-api" or "{org}/my-api"
 *     branch?:    string,              // default: "main"
 *     name?:      string,              // default: derived from repo
 *     ports?:     string,              // default: "3000"
 *     buildPack?: "nixpacks"|"static"|"dockerfile"|"dockercompose"
 *     domain?:    string,              // default: {app}.{workspace}.vibnai.com
 *     envs?:      Record<string,string>
 *     instantDeploy?: boolean,         // default: true
 *   }
 */

import { NextResponse } from "next/server";
import { requireWorkspacePrincipal } from "@/lib/auth/workspace-auth";
import {
  listApplicationsInProject,
  projectUuidOf,
  createPublicApp,
  upsertApplicationEnv,
  getApplication,
  deployApplication,
} from "@/lib/coolify";
import {
  slugify,
  workspaceAppFqdn,
  toDomainsString,
  isDomainUnderWorkspace,
  giteaHttpsUrl,
} from "@/lib/naming";
import { getRepo } from "@/lib/gitea";
import { getWorkspaceBotCredentials } from "@/lib/workspaces";

export async function GET(
  request: Request,
  { params }: { params: Promise<{ slug: string }> },
) {
  const { slug } = await params;
  const principal = await requireWorkspacePrincipal(request, {
    targetSlug: slug,
  });
  if (principal instanceof NextResponse) return principal;

  const ws = principal.workspace;
  if (!ws.coolify_project_uuid) {
    return NextResponse.json(
      { error: "Workspace has no Coolify project yet", apps: [] },
      { status: 503 },
    );
  }

  try {
    const apps = await listApplicationsInProject(ws.coolify_project_uuid);
    return NextResponse.json({
      workspace: { slug: ws.slug, coolifyProjectUuid: ws.coolify_project_uuid },
      apps: apps.map((a) => ({
        uuid: a.uuid,
        name: a.name,
        status: a.status,
        fqdn: a.fqdn ?? null,
        domains: a.domains ?? null,
        gitRepository: a.git_repository ?? null,
        gitBranch: a.git_branch ?? null,
        projectUuid: projectUuidOf(a),
      })),
    });
  } catch (err) {
    return NextResponse.json(
      {
        error: "Coolify request failed",
        details: err instanceof Error ? err.message : String(err),
      },
      { status: 502 },
    );
  }
}

export async function POST(
  request: Request,
  { params }: { params: Promise<{ slug: string }> },
) {
  const { slug } = await params;
  const principal = await requireWorkspacePrincipal(request, {
    targetSlug: slug,
  });
  if (principal instanceof NextResponse) return principal;

  const ws = principal.workspace;
  if (
    !ws.coolify_project_uuid ||
    !ws.coolify_private_key_uuid ||
    !ws.gitea_org
  ) {
    return NextResponse.json(
      {
        error:
          "Workspace not fully provisioned (need Coolify project + deploy key + Gitea org)",
      },
      { status: 503 },
    );
  }

  type Body = {
    repo?: string;
    branch?: string;
    name?: string;
    ports?: string;
    buildPack?: "nixpacks" | "static" | "dockerfile" | "dockercompose";
    domain?: string;
    envs?: Record<string, string>;
    instantDeploy?: boolean;
    description?: string;
    baseDirectory?: string;
    installCommand?: string;
    buildCommand?: string;
    startCommand?: string;
    dockerfileLocation?: string;
  };
  let body: Body = {};
  try {
    body = (await request.json()) as Body;
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }

  if (!body.repo || typeof body.repo !== "string") {
    return NextResponse.json(
      { error: 'Missing "repo" field' },
      { status: 400 },
    );
  }

  // Accept either "repo-name" (assumed in workspace org) or "org/repo".
  const parts = body.repo.replace(/\.git$/, "").split("/");
  const repoOrg = parts.length === 2 ? parts[0] : ws.gitea_org;
  const repoName = parts.length === 2 ? parts[1] : parts[0];
  if (repoOrg !== ws.gitea_org) {
    return NextResponse.json(
      {
        error: `Repo owner ${repoOrg} is not this workspace's org ${ws.gitea_org}`,
      },
      { status: 403 },
    );
  }
  if (!/^[a-zA-Z0-9._-]+$/.test(repoName)) {
    return NextResponse.json({ error: "Invalid repo name" }, { status: 400 });
  }

  // Verify the repo actually exists in Gitea (fail fast).
  const repo = await getRepo(repoOrg, repoName);
  if (!repo) {
    return NextResponse.json(
      { error: `Repo ${repoOrg}/${repoName} not found in Gitea` },
      { status: 404 },
    );
  }

  const creds = getWorkspaceBotCredentials(ws);
  if (!creds) {
    return NextResponse.json(
      { error: "Workspace Gitea bot credentials not fully provisioned" },
      { status: 503 },
    );
  }

  const appName = slugify(body.name ?? repoName);
  const fqdn = body.domain
    ? body.domain.replace(/^https?:\/\//, "")
    : workspaceAppFqdn(ws.slug, appName);
  if (!isDomainUnderWorkspace(fqdn, ws.slug)) {
    return NextResponse.json(
      { error: `Domain ${fqdn} must end with .${ws.slug}.vibnai.com` },
      { status: 403 },
    );
  }

  try {
    const created = await createPublicApp({
      projectUuid: ws.coolify_project_uuid,
      serverUuid: ws.coolify_server_uuid ?? undefined,
      environmentName: ws.coolify_environment_name,
      destinationUuid: ws.coolify_destination_uuid ?? undefined,
      gitRepository: giteaHttpsUrl(
        repoOrg,
        repoName,
        creds.username,
        creds.token,
      ),
      gitBranch: body.branch ?? repo.default_branch ?? "main",
      portsExposes: body.ports ?? "3000",
      buildPack: body.buildPack ?? "nixpacks",
      name: appName,
      description: body.description ?? `AI-created from ${repoOrg}/${repoName}`,
      domains: toDomainsString([fqdn]),
      isAutoDeployEnabled: true,
      isForceHttpsEnabled: true,
      // We defer the first deploy until envs are attached so they
      // show up in the initial build.
      instantDeploy: false,
      baseDirectory: body.baseDirectory,
      installCommand: body.installCommand,
      buildCommand: body.buildCommand,
      startCommand: body.startCommand,
      dockerfileLocation: body.dockerfileLocation,
    });

    // Attach env vars (best-effort — don't fail the whole create on one bad key).
    if (body.envs && typeof body.envs === "object") {
      for (const [key, value] of Object.entries(body.envs)) {
        if (!/^[A-Z_][A-Z0-9_]*$/i.test(key)) continue;
        try {
          await upsertApplicationEnv(created.uuid, { key, value });
        } catch (e) {
          console.warn("[apps.POST] upsert env failed", key, e);
        }
      }
    }

    // Now kick off the first deploy (unless the caller opted out).
    let deploymentUuid: string | null = null;
    if (body.instantDeploy !== false) {
      try {
        const dep = await deployApplication(created.uuid);
        deploymentUuid = dep.deployment_uuid ?? null;
      } catch (e) {
        console.warn("[apps.POST] initial deploy failed", e);
      }
    }

    // Return a hydrated object (status / urls) for the UI.
    const app = await getApplication(created.uuid);
    return NextResponse.json(
      {
        uuid: app.uuid,
        name: app.name,
        status: app.status,
        domain: fqdn,
        url: `https://${fqdn}`,
        gitRepository: app.git_repository ?? null,
        gitBranch: app.git_branch ?? null,
        deploymentUuid,
      },
      { status: 201 },
    );
  } catch (err) {
    return NextResponse.json(
      {
        error: "Coolify create failed",
        details: err instanceof Error ? err.message : String(err),
      },
      { status: 502 },
    );
  }
}
