import { NextResponse } from "next/server";
import { createAdminClient } from "@/lib/supabase-admin";
import {
  extractMercadoPagoQrData,
  fetchMercadoPagoApi,
  getActiveMercadoPagoCredentials,
  normalizeMercadoPagoPaymentMethod,
  normalizeMercadoPagoPaymentStatus,
  parseMercadoPagoSettings,
} from "@/lib/mercado-pago";
import { applyApprovedCatalogPayment } from "@/lib/catalog-payment-processing";
import { reconcileCatalogPaymentWithProvider } from "@/lib/catalog-payment-reconcile";
import { applyApprovedPaymentLinkPayment } from "@/lib/payment-link-processing";
import { normalizeMercadoPagoSubscriptionStatus } from "@/lib/payment-links";

function normalizeText(value: unknown) {
  return String(value || "").trim();
}

type WebhookPayload = Record<string, unknown> & {
  data?: Record<string, unknown>;
  resource?: unknown;
  id?: unknown;
  type?: unknown;
  topic?: unknown;
};

type MercadoPagoPaymentResponse = Record<string, unknown> & {
  status?: unknown;
  status_detail?: unknown;
  payment_method_id?: unknown;
  payment_type_id?: unknown;
  date_approved?: unknown;
  date_of_expiration?: unknown;
};

type MercadoPagoSubscriptionResponse = Record<string, unknown> & {
  status?: unknown;
};

function readPaymentId(payload: WebhookPayload, requestUrl: URL) {
  const resourceValue = typeof payload.resource === "string" ? payload.resource : "";
  const bodyId =
    normalizeText(payload.data?.id) || normalizeText(resourceValue.split("/").pop()) || normalizeText(payload.id);
  const queryId = normalizeText(requestUrl.searchParams.get("data.id") || requestUrl.searchParams.get("id"));
  return bodyId || queryId;
}

function readEventType(payload: WebhookPayload, requestUrl: URL) {
  return normalizeText(payload.type || payload.topic || requestUrl.searchParams.get("type") || requestUrl.searchParams.get("topic")).toLowerCase();
}

async function syncSubscriptionWebhook(params: {
  admin: ReturnType<typeof createAdminClient>;
  subscriptionId: string;
  payload: WebhookPayload;
}) {
  const { admin, subscriptionId, payload } = params;
  const { data: localSubscription, error: localSubscriptionError } = await admin
    .from("payment_link_subscriptions")
    .select("id,user_id,provider,subscription_id")
    .eq("provider", "mercado_pago")
    .eq("subscription_id", subscriptionId)
    .maybeSingle();

  if (localSubscriptionError || !localSubscription?.id) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const integration = await admin
    .from("integrations")
    .select("id,settings,active")
    .eq("provider", "mercado_pago")
    .eq("user_id", localSubscription.user_id)
    .maybeSingle();

  if (integration.error || !integration.data?.id || integration.data.active === false) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const settings = parseMercadoPagoSettings(integration.data.settings);
  const credentials = getActiveMercadoPagoCredentials(settings);
  if (!credentials) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const remoteSubscription = await fetchMercadoPagoApi<MercadoPagoSubscriptionResponse>(
    `/preapproval/${encodeURIComponent(subscriptionId)}`,
    credentials.accessToken,
    { method: "GET" }
  );

  const normalizedStatus = normalizeMercadoPagoSubscriptionStatus(remoteSubscription.status);
  const now = new Date().toISOString();

  await admin
    .from("payment_link_subscriptions")
    .update({
      status: normalizedStatus,
      started_at: normalizedStatus === "authorized" ? now : null,
      paused_at: normalizedStatus === "paused" ? now : null,
      cancelled_at: normalizedStatus === "cancelled" ? now : null,
      raw_response: remoteSubscription,
      last_webhook_payload: payload,
      updated_at: now,
    })
    .eq("id", localSubscription.id);

  if (normalizedStatus === "authorized") {
    const { data: subscriptionLink } = await admin
      .from("payment_link_subscriptions")
      .select("payment_link_id")
      .eq("id", localSubscription.id)
      .maybeSingle();

    if (subscriptionLink?.payment_link_id) {
      await admin
        .from("payment_links")
        .update({ last_paid_at: now, updated_at: now })
        .eq("id", subscriptionLink.payment_link_id);
    }
  }

  return NextResponse.json({ ok: true });
}

async function syncPaymentLinkWebhook(params: {
  admin: ReturnType<typeof createAdminClient>;
  localPayment: {
    id: string;
    user_id: string;
    integration_id: string | null;
    payment_link_id: string;
  };
  paymentId: string;
  payload: WebhookPayload;
}) {
  const { admin, localPayment, paymentId, payload } = params;
  if (!localPayment.integration_id) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const { data: integration, error: integrationError } = await admin
    .from("integrations")
    .select("id,settings,active")
    .eq("id", localPayment.integration_id)
    .maybeSingle();

  if (integrationError || !integration?.id || integration.active === false) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const settings = parseMercadoPagoSettings(integration.settings);
  const credentials = getActiveMercadoPagoCredentials(settings);
  if (!credentials) {
    return NextResponse.json({ ok: true, ignored: true });
  }

  const remotePayment = await fetchMercadoPagoApi<MercadoPagoPaymentResponse>(
    `/v1/payments/${encodeURIComponent(paymentId)}`,
    credentials.accessToken,
    { method: "GET" }
  );

  const normalizedStatus = normalizeMercadoPagoPaymentStatus(remotePayment.status);
  const normalizedMethod = normalizeMercadoPagoPaymentMethod(remotePayment.payment_method_id, remotePayment.payment_type_id);
  const qrData = extractMercadoPagoQrData(remotePayment);
  const now = new Date().toISOString();

  await admin
    .from("payment_link_payments")
    .update({
      status: normalizedStatus,
      provider_status: normalizeText(remotePayment.status) || null,
      provider_status_detail: normalizeText(remotePayment.status_detail) || null,
      payment_method: normalizedMethod,
      payment_type: normalizeText(remotePayment.payment_type_id) || null,
      approved_at:
        normalizedStatus === "approved"
          ? normalizeText(remotePayment.date_approved) || now
          : null,
      rejected_at: normalizedStatus === "rejected" ? now : null,
      cancelled_at: normalizedStatus === "cancelled" ? now : null,
      expires_at: normalizeText(remotePayment.date_of_expiration) || null,
      qr_code: qrData.qrCode,
      qr_code_base64: qrData.qrCodeBase64,
      ticket_url: qrData.ticketUrl,
      raw_response: remotePayment,
      last_webhook_payload: payload,
      updated_at: now,
    })
    .eq("id", localPayment.id);

  if (normalizedStatus === "approved") {
    await admin
      .from("payment_links")
      .update({ last_paid_at: now, updated_at: now })
      .eq("id", localPayment.payment_link_id);

    await applyApprovedPaymentLinkPayment(admin as never, String(localPayment.id));
  }

  return NextResponse.json({ ok: true });
}

async function sincronizarCobrancaDoBalcao(params: {
  admin: ReturnType<typeof createAdminClient>;
  cobranca: { id: string; tenant_id: string; integration_id: string | null; status: string };
  paymentId: string;
}) {
  const { admin, cobranca, paymentId } = params;
  if (cobranca.status !== "pending" || !cobranca.integration_id) return;

  const { data: integration } = await admin
    .from("integrations")
    .select("id,settings,active")
    .eq("id", cobranca.integration_id)
    .maybeSingle();
  if (!integration?.id || integration.active === false) return;

  const credentials = getActiveMercadoPagoCredentials(parseMercadoPagoSettings(integration.settings));
  if (!credentials) return;

  const remotePayment = await fetchMercadoPagoApi<MercadoPagoPaymentResponse>(
    `/v1/payments/${encodeURIComponent(paymentId)}`,
    credentials.accessToken,
    { method: "GET" }
  );

  const estado = normalizeMercadoPagoPaymentStatus(remotePayment.status);
  const detalhe = normalizeText(remotePayment.status_detail);
  const now = new Date().toISOString();
  const novoStatus =
    estado === "approved"
      ? "approved"
      : estado === "pending"
        ? "pending"
        : estado === "rejected"
          ? "rejected"
          : detalhe.toLowerCase().includes("expired")
            ? "expired"
            : "cancelled";

  await admin
    .from("pdv_pix_charges")
    .update({
      status: novoStatus,
      provider_status: normalizeText(remotePayment.status) || null,
      provider_status_detail: detalhe || null,
      approved_at: novoStatus === "approved" ? normalizeText(remotePayment.date_approved) || now : null,
      cancelled_at: novoStatus === "cancelled" || novoStatus === "expired" ? now : null,
      checked_at: now,
      raw_response: remotePayment,
      updated_at: now,
    })
    .eq("id", cobranca.id)
    /* So enquanto pendente: um cancelamento feito no balcao no mesmo instante
       nao pode ser desfeito por um aviso atrasado. */
    .eq("status", "pending");
}

export async function POST(request: Request) {
  try {
    const payload = (await request.json().catch(() => ({}))) as WebhookPayload;
    const requestUrl = new URL(request.url);
    const paymentId = readPaymentId(payload, requestUrl);
    const eventType = readEventType(payload, requestUrl);
    const isSubscriptionEvent =
      eventType.includes("subscription") ||
      eventType.includes("preapproval") ||
      eventType === "automatic-payments";

    if (!paymentId) {
      return NextResponse.json({ ok: true, ignored: true });
    }

    if (isSubscriptionEvent) {
      const admin = createAdminClient();
      return await syncSubscriptionWebhook({
        admin,
        subscriptionId: paymentId,
        payload,
      });
    }

    if (eventType && eventType !== "payment") {
      return NextResponse.json({ ok: true, ignored: true });
    }

    const admin = createAdminClient();
    const { data: linkPayment } = await admin
      .from("payment_link_payments")
      .select("id,user_id,integration_id,payment_link_id,provider_payment_id,status")
      .eq("provider", "mercado_pago")
      .eq("provider_payment_id", paymentId)
      .maybeSingle();

    if (linkPayment?.id) {
      return await syncPaymentLinkWebhook({
        admin,
        localPayment: linkPayment,
        paymentId,
        payload,
      });
    }

    /*
      Pix cobrado no balcão (PDV do admin). O registro fica em
      `pdv_pix_charges`, e o balcão também confere sozinho de 4 em 4 s; o
      aviso do provedor só encurta a espera. Aqui não se cria venda nenhuma —
      quem fecha a venda é o checkout do PDV, quando vê a cobrança aprovada.
    */
    const { data: cobrancaDoBalcao } = await admin
      .from("pdv_pix_charges")
      .select("id,tenant_id,integration_id,status")
      .eq("provider", "mercado_pago")
      .eq("provider_payment_id", paymentId)
      .maybeSingle();

    if (cobrancaDoBalcao?.id) {
      await sincronizarCobrancaDoBalcao({ admin, cobranca: cobrancaDoBalcao, paymentId });
      return NextResponse.json({ ok: true });
    }

    const { data: localPayment, error: localPaymentError } = await admin
      .from("catalog_order_payments")
      .select("id,user_id,catalog_order_id,amount,integration_id,provider_payment_id,status,updated_at")
      .eq("provider", "mercado_pago")
      .eq("provider_payment_id", paymentId)
      .maybeSingle();

    if (localPaymentError || !localPayment?.id || !localPayment.integration_id) {
      return NextResponse.json({ ok: true, ignored: true });
    }

    /* A mesma rotina que a consulta de status usa. Antes este trecho era uma
       copia da conciliacao dentro do webhook; com duas copias, qualquer ajuste
       numa delas deixava a outra para tras. `forcar` porque aviso do provedor
       nao espera intervalo. */
    await reconcileCatalogPaymentWithProvider(admin as never, localPayment as never, {
      webhookPayload: payload,
      forcar: true,
    });

    return NextResponse.json({ ok: true });
  } catch {
    return NextResponse.json({ ok: true });
  }
}
