import { requireSupportWrite } from "@/lib/impersonate/support";
import { createTenantSchema } from "@/lib/schemas/tenant-creation";
import { issueInvite } from "@/lib/auth/issue-invite";
import { type NextRequest } from "next/server";
import { z } from "zod";
import { falhaDaEscritaDePlatformAdmin, requirePlatformAdmin, requirePlatformAdminEscrita, type PlatformAdminContext } from "@/lib/auth/requirePlatformAdmin";
import { createAdminClient } from "@/lib/supabase/admin";
import { ok, fail } from "@/lib/api/wrappers";
import { audit } from "@/lib/audit";
import { createHash, randomUUID } from "node:crypto";
import { lerPlano } from "@/lib/cobranca/dono";
import { moduloLigado } from "@/lib/instalacao/modulos";
import { querySchema, listarTenants } from "./_lista";

// ---------------------------------------------------------------------------
// GET /api/v1/admin/tenants
//
// `querySchema`, os cursores e o `WHERE` moram em `_lista.ts`: é o MESMO
// leitor que `export/route.ts` usa, para exportar devolver o que a tela mostra
// (issue #2777). Ver o cabeçalho de lá.
// ---------------------------------------------------------------------------

export async function GET(req: NextRequest) {
  const requestId = randomUUID();

  let adminCtx: Awaited<ReturnType<typeof requirePlatformAdmin>>;
  try {
    adminCtx = await requirePlatformAdmin();
  } catch (err) {
    return falhaDaEscritaDePlatformAdmin(err, requestId);
  }

  const parsed = querySchema.safeParse(Object.fromEntries(req.nextUrl.searchParams.entries()));
  if (!parsed.success) {
    return fail("validation_error", "Invalid query params", 400, {
      requestId,
      details: parsed.error.flatten(),
    });
  }

  const { q, status, cursor, limit } = parsed.data;
  const admin = createAdminClient();
  const { page, has_more, nextCursor, error } = await listarTenants(admin, {
    q,
    status,
    cursor,
    limit,
  });

  if (error) {
    return fail("internal_error", "Query failed", 500, {
      requestId,
      details: error.message,
    });
  }

  void audit({
    action: "platform_admin.tenants_listed",
    actorUserId: adminCtx.user.id,
    actingAsPlatformAdmin: true,
    bypassedRls: true,
    requestId,
    metadata: {
      filters: { status: status ?? null, has_q: !!q },
      result_count: page.length,
    },
  });

  return ok(page, {
    requestId,
    meta: { has_more, cursor: nextCursor },
  });
}

// ---------------------------------------------------------------------------
// POST /api/v1/admin/tenants
// ---------------------------------------------------------------------------

export async function POST(req: NextRequest) {
  const supportDenied = await requireSupportWrite();
  if (supportDenied) return supportDenied;

  const requestId = randomUUID();

  let adminCtx: PlatformAdminContext;
  try {
    adminCtx = await requirePlatformAdminEscrita();
  } catch (err) {
    return falhaDaEscritaDePlatformAdmin(err, requestId);
  }
  const key = req.headers.get("Idempotency-Key") ?? randomUUID();
  if (!z.string().uuid().safeParse(key).success) {
    return fail("validation_error", "Idempotency-Key deve ser UUID", 400, { requestId });
  }

  let body: unknown;
  try {
    body = await req.json();
  } catch {
    return fail("validation_error", "Invalid JSON body", 400, { requestId });
  }

  const parsed = createTenantSchema.safeParse(body);
  if (!parsed.success) {
    return fail("validation_error", "Invalid request body", 400, {
      requestId,
      details: parsed.error.flatten(),
    });
  }

  const admin = createAdminClient();
  // Spec da cobrança §2.1/§9 (D-2): com a cobrança ligada, o dono escolhe um
  // plano de cobrança (ou nenhum: isenta) e o rótulo antigo `settings.plan`
  // deixa de ser gravado. Desligada, tudo segue como antes, e `plano_id` é
  // recusado aqui, com nome (a função SQL também o recusa).
  const cobrancaLigada = await moduloLigado(admin, "cobranca");
  if (parsed.data.plano_id !== undefined) {
    const plano = cobrancaLigada ? await lerPlano(admin, parsed.data.plano_id) : null;
    if (plano === "erro") return fail("internal_error", "Não foi possível ler o plano", 500, { requestId });
    if (!plano || plano.arquivado_em !== null) {
      return fail(
        "plano_invalido",
        cobrancaLigada ? "Plano não encontrado ou arquivado." : "A cobrança está desligada nesta instalação.",
        422,
        { requestId },
      );
    }
  }
  const normalizado = { ...parsed.data, owner_email: parsed.data.owner_email.trim().toLowerCase() };
  const request = cobrancaLigada ? { ...normalizado, plan: undefined } : normalizado;
  const { data: org, error } = await admin.rpc("fn_create_tenant_with_owner", {
    p_actor: adminCtx.user.id,
    p_key: key,
    p_request: request,
    p_hash: createHash("sha256").update(JSON.stringify(request)).digest("hex"),
  });
  if (error) {
    // Corrida: a chave desligou ou o plano foi arquivado entre a leitura acima e
    // a função (Task 5 levanta 22023 com a mensagem). Sem isto, o 22023 caía no
    // "slug já existe" abaixo.
    if (error.code === "22023" && /cobranca_desligada|plano_invalido/.test(error.message ?? "")) {
      const desligou = (error.message ?? "").includes("cobranca_desligada");
      return fail(
        "plano_invalido",
        desligou ? "A cobrança está desligada nesta instalação." : "Plano não encontrado ou arquivado.",
        422,
        { requestId },
      );
    }
    if (error.code === "23505" || error.code === "22023") {
      return fail("conflict", "Slug já existe ou a chave foi usada com outros dados", 409, {
        requestId,
      });
    }
    return fail("internal_error", "Não foi possível criar a organização", 500, { requestId });
  }
  if (org.created) {
    await audit({
      action: "tenant.created_by_platform_admin",
      actorUserId: adminCtx.user.id,
      actingAsPlatformAdmin: true,
      bypassedRls: true,
      organizationId: org.id,
      resourceType: "organization",
      resourceId: org.id,
      requestId,
      metadata: {
        slug: org.slug,
        display_name: org.display_name,
        plan: request.plan ?? null,
        plano_id: request.plano_id ?? null,
        creator_role: "admin",
      },
    });
  }
  const ownerInvitation =
    request.owner_email === adminCtx.user.email?.trim().toLowerCase()
      ? null
      : await issueInvite({
          email: request.owner_email,
          role: "admin",
          interfaceSettings: request.owner_interface_settings,
          organizationId: org.id,
          orgName: org.display_name,
          inviterId: adminCtx.user.id,
          inviterName:
            adminCtx.user.user_metadata?.full_name ?? adminCtx.user.email ?? "Administrador",
          requestId,
          inviteId: org.invite_id,
          issuedAt: org.issued_at,
          dispatch: org.created,
        });
  return ok(
    {
      id: org.id,
      slug: org.slug,
      display_name: org.display_name,
      owner_invitation: ownerInvitation,
    },
    { status: 201, requestId },
  );
}
