// disconnect-integration (autenticada JWT)
// Lógica:
//   1. Checa cronjobs dependentes — se houver e force_pause_jobs=false, retorna 409.
//   2. Se force_pause_jobs=true, pausa todos os jobs dependentes.
//   3. Revoga token no provider (1 retry em timeout).
//   4. Se revoke 5xx/timeout: NÃO deleta nada, marca status='error', retorna 503.
//   5. Sucesso: deleta secrets do Vault e a row de user_integrations.
//
// TODO Fase 5: notify Hermes container to invalidate MCP config after disconnect.

import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4";
import { corsHeaders } from "../_shared/cors.ts";
import { revokeToken, type ProviderSlug } from "../_shared/oauth-providers.ts";

function jsonResponse(body: unknown, status = 200): Response {
  return new Response(JSON.stringify(body), {
    status,
    headers: { ...corsHeaders, "Content-Type": "application/json" },
  });
}

async function getDecryptedSecret(
  admin: ReturnType<typeof createClient>,
  secretId: string,
): Promise<string | null> {
  const { data, error } = await admin
    .rpc("vault_decrypt_secret", { secret_id: secretId })
    .single();
  if (!error && data) {
    // deno-lint-ignore no-explicit-any
    return (data as any).decrypted_secret ?? (data as unknown as string);
  }
  return null;
}

Deno.serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
    const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
    const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;

    const authHeader = req.headers.get("Authorization") ?? "";
    const userClient = createClient(supabaseUrl, anonKey, {
      global: { headers: { Authorization: authHeader } },
    });
    const { data: userData, error: userErr } = await userClient.auth.getUser();
    if (userErr || !userData.user) {
      return jsonResponse({ error: "Não autenticado" }, 401);
    }
    const userId = userData.user.id;

    const { integration_id, force_pause_jobs } = await req.json() as {
      integration_id?: string;
      force_pause_jobs?: boolean;
    };
    if (!integration_id) {
      return jsonResponse({ error: "integration_id é obrigatório" }, 400);
    }

    const admin = createClient(supabaseUrl, serviceKey);

    const { data: integ, error: iErr } = await admin
      .from("user_integrations")
      .select(
        "id, user_id, access_token_vault_id, refresh_token_vault_id, mcp:available_mcps(slug, oauth_revoke_url)",
      )
      .eq("id", integration_id)
      .eq("user_id", userId)
      .maybeSingle();

    if (iErr || !integ) {
      return jsonResponse({ error: "Integração não encontrada" }, 404);
    }

    // deno-lint-ignore no-explicit-any
    const mcp = (integ as any).mcp as { slug: string; oauth_revoke_url: string | null };
    const slug = mcp.slug;

    // 1. Checa dependências
    const { data: dependentJobs } = await admin
      .from("scheduled_jobs")
      .select("id, name")
      .eq("user_id", userId)
      .neq("status", "archived")
      .contains("required_mcp_slugs", [slug]);

    if (dependentJobs && dependentJobs.length > 0 && !force_pause_jobs) {
      return jsonResponse(
        {
          error: "has_dependencies",
          dependent_jobs: dependentJobs,
        },
        409,
      );
    }

    let pausedCount = 0;
    if (dependentJobs && dependentJobs.length > 0 && force_pause_jobs) {
      const { error: pErr } = await admin
        .from("scheduled_jobs")
        .update({
          status: "paused",
          auto_paused_reason: `Integração ${slug} foi desconectada`,
        })
        .eq("user_id", userId)
        .neq("status", "archived")
        .contains("required_mcp_slugs", [slug]);
      if (!pErr) pausedCount = dependentJobs.length;
    }

    // 2. Busca tokens ANTES de qualquer delete
    const accessToken = integ.access_token_vault_id
      ? await getDecryptedSecret(admin, integ.access_token_vault_id)
      : null;

    // 3. Revoga no provider
    if (accessToken) {
      const revokeResult = await revokeToken(
        slug as ProviderSlug,
        accessToken,
        mcp.oauth_revoke_url,
      );

      if (revokeResult.serverError) {
        // NÃO deleta nada. Marca como erro.
        await admin
          .from("user_integrations")
          .update({
            status: "error",
            error_message: "Revoke falhou no provider. Tente novamente em alguns segundos.",
          })
          .eq("id", integration_id);
        return jsonResponse(
          { error: "revoke_failed", message: "O provider está indisponível. Tente novamente em instantes." },
          503,
        );
      }
    }

    // 4. Sucesso: deleta secrets e row
    const vaultIds = [integ.access_token_vault_id, integ.refresh_token_vault_id]
      .filter((id): id is string => Boolean(id));

    for (const vid of vaultIds) {
      try {
        await admin.rpc("vault_delete_secret", { secret_id: vid });
      } catch (e) {
        console.warn("vault_delete_secret ignored", e instanceof Error ? e.message : "unknown");
      }
    }

    const { error: dErr } = await admin
      .from("user_integrations")
      .delete()
      .eq("id", integration_id);

    if (dErr) {
      console.error("delete user_integrations error", dErr.message);
      return jsonResponse({ error: "Falha ao remover integração" }, 500);
    }

    // TODO Fase 5: notify Hermes container that MCP was disconnected
    return jsonResponse({ success: true, paused_jobs_count: pausedCount });
  } catch (err) {
    console.error("disconnect-integration fatal", err instanceof Error ? err.message : "unknown");
    return jsonResponse(
      { error: err instanceof Error ? err.message : "Erro inesperado" },
      500,
    );
  }
});
