/**
 * GET  /api/workspaces/[slug]/apps/[uuid]/envs — list env vars
 * PATCH /api/workspaces/[slug]/apps/[uuid]/envs — upsert one env var
 *   body: { key, value, is_preview?, is_literal?, is_multiline?, is_shown_once? }
 * DELETE /api/workspaces/[slug]/apps/[uuid]/envs?key=FOO — delete one env var
 *
 * Tenant boundary: the app must belong to the workspace's Coolify project.
 *
 * NOTE: `is_build_time` is **not** a writable flag in Coolify v4 — it's a
 * derived read-only attribute. We silently drop it from incoming request
 * bodies for back-compat with older agents; the value is computed by
 * Coolify at build time based on Dockerfile ARG usage.
 */

import { NextResponse } from 'next/server';
import { requireWorkspacePrincipal } from '@/lib/auth/workspace-auth';
import {
  deleteApplicationEnv,
  getApplicationInProject,
  listApplicationEnvs,
  TenantError,
  upsertApplicationEnv,
} from '@/lib/coolify';

async function verify(request: Request, slug: string, uuid: string) {
  const principal = await requireWorkspacePrincipal(request, { targetSlug: slug });
  if (principal instanceof NextResponse) return { error: principal };
  const ws = principal.workspace;
  if (!ws.coolify_project_uuid) {
    return {
      error: NextResponse.json(
        { error: 'Workspace has no Coolify project yet' },
        { status: 503 }
      ),
    };
  }
  try {
    await getApplicationInProject(uuid, ws.coolify_project_uuid);
  } catch (err) {
    if (err instanceof TenantError) {
      return { error: NextResponse.json({ error: err.message }, { status: 403 }) };
    }
    return {
      error: NextResponse.json(
        { error: 'Coolify request failed', details: String(err) },
        { status: 502 }
      ),
    };
  }
  return { principal };
}

export async function GET(
  request: Request,
  { params }: { params: Promise<{ slug: string; uuid: string }> }
) {
  const { slug, uuid } = await params;
  const check = await verify(request, slug, uuid);
  if ('error' in check) return check.error;

  try {
    const envs = await listApplicationEnvs(uuid);
    // Redact values by default for API-key callers — they can re-fetch
    // with ?reveal=true when they need the actual values (e.g. to copy
    // a DATABASE_URL). Session callers always get full values.
    const url = new URL(request.url);
    const reveal =
      check.principal.source === 'session' || url.searchParams.get('reveal') === 'true';
    return NextResponse.json({
      envs: envs.map(e => ({
        key: e.key,
        value: reveal ? e.value : maskValue(e.value),
        isPreview: e.is_preview ?? false,
        // Coolify spells the read-only build-time flag two different ways
        // depending on version — `is_buildtime` (new, one word) and
        // `is_build_time` (old, underscored). Fall through both.
        isBuildTime: e.is_buildtime ?? e.is_build_time ?? false,
        isRuntime: e.is_runtime ?? true,
        isLiteral: e.is_literal ?? false,
        isMultiline: e.is_multiline ?? false,
      })),
    });
  } catch (err) {
    return NextResponse.json(
      { error: 'Coolify request failed', details: String(err) },
      { status: 502 }
    );
  }
}

export async function PATCH(
  request: Request,
  { params }: { params: Promise<{ slug: string; uuid: string }> }
) {
  const { slug, uuid } = await params;
  const check = await verify(request, slug, uuid);
  if ('error' in check) return check.error;

  let body: {
    key?: string;
    value?: string;
    is_preview?: boolean;
    /** @deprecated silently dropped — Coolify no longer accepts this on write. */
    is_build_time?: boolean;
    is_literal?: boolean;
    is_multiline?: boolean;
    is_shown_once?: boolean;
  };
  try {
    body = await request.json();
  } catch {
    return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
  }

  if (!body.key || typeof body.value !== 'string') {
    return NextResponse.json({ error: 'Fields "key" and "value" are required' }, { status: 400 });
  }

  try {
    const env = await upsertApplicationEnv(uuid, {
      key: body.key,
      value: body.value,
      is_preview: body.is_preview ?? false,
      is_literal: body.is_literal ?? false,
      is_multiline: body.is_multiline ?? false,
      is_shown_once: body.is_shown_once ?? false,
    });
    return NextResponse.json({
      ok: true,
      key: env.key,
      // Soft-deprecation signal so the caller's agent can learn to stop
      // sending the flag without hard-breaking today.
      warnings:
        body.is_build_time !== undefined
          ? [
              'is_build_time is ignored — Coolify derives build-vs-runtime from Dockerfile ARG usage. Omit this field going forward.',
            ]
          : undefined,
    });
  } catch (err) {
    return NextResponse.json(
      { error: 'Coolify request failed', details: String(err) },
      { status: 502 }
    );
  }
}

export async function DELETE(
  request: Request,
  { params }: { params: Promise<{ slug: string; uuid: string }> }
) {
  const { slug, uuid } = await params;
  const check = await verify(request, slug, uuid);
  if ('error' in check) return check.error;

  const key = new URL(request.url).searchParams.get('key');
  if (!key) {
    return NextResponse.json({ error: 'Query param "key" is required' }, { status: 400 });
  }

  try {
    await deleteApplicationEnv(uuid, key);
    return NextResponse.json({ ok: true, key });
  } catch (err) {
    return NextResponse.json(
      { error: 'Coolify request failed', details: String(err) },
      { status: 502 }
    );
  }
}

function maskValue(v: string): string {
  if (!v) return '';
  if (v.length <= 4) return '•'.repeat(v.length);
  return `${v.slice(0, 2)}${'•'.repeat(Math.min(v.length - 4, 10))}${v.slice(-2)}`;
}
