import { randomBytes } from "node:crypto";
import type { SupabaseClient, User } from "@supabase/supabase-js";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { getPublicCatalogOrigin, normalizeCustomDomain } from "@/lib/public-catalog-url";
import { createAdminClient } from "@/lib/supabase-server";
import { ApiV1Error } from "@/lib/api-v1/errors";

type PaymentLinkStatus = "draft" | "active" | "paused" | "archived";
type PaymentLinkType = "one_time" | "subscription";

type PaymentLinkRow = {
  id: string;
  user_id: string;
  company_id: string | null;
  slug: string;
  code: string;
  status: PaymentLinkStatus;
  type: PaymentLinkType;
  title: string;
  description: string | null;
  amount: number | string | null;
  currency: string | null;
  image_url: string | null;
  interval_count: number | null;
  interval_unit: string | null;
  allow_credit_card: boolean | null;
  allow_pix: boolean | null;
  allow_boleto: boolean | null;
  checkout_config: Record<string, unknown> | null;
  share_config: Record<string, unknown> | null;
  metadata: Record<string, unknown> | null;
  last_paid_at: string | null;
  last_opened_at: string | null;
  expires_at: string | null;
  created_at: string;
  updated_at: string;
};

type PaymentLinkSubscriptionRow = {
  id: string;
  payment_link_id: string;
  status: string;
  amount: number | string | null;
  created_at: string;
};

export type PaymentLinkRequestContext = {
  user: User;
  ctx: Awaited<ReturnType<typeof getCompanyContext>>;
  admin: ReturnType<typeof createAdminClient>;
  ownerId: string;
  origin: string;
  slug: string;
};

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

function normalizeMoney(value: unknown) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) return 0;
  return Number(numeric.toFixed(2));
}

function normalizeBoolean(value: unknown, fallback = false) {
  if (typeof value === "boolean") return value;
  if (typeof value === "string") {
    const normalized = value.trim().toLowerCase();
    if (normalized === "true") return true;
    if (normalized === "false") return false;
  }
  return fallback;
}

function normalizeStatus(value: unknown): PaymentLinkStatus {
  return value === "active" || value === "paused" || value === "archived" ? value : "draft";
}

function normalizeType(value: unknown): PaymentLinkType {
  return value === "subscription" ? "subscription" : "one_time";
}

function normalizeIntervalUnit(value: unknown) {
  return value === "days" || value === "months" || value === "years" ? value : null;
}

function randomCodeSegment(length: number) {
  const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  const bytes = randomBytes(length);
  let output = "";
  for (let index = 0; index < length; index += 1) {
    output += alphabet[bytes[index] % alphabet.length];
  }
  return output;
}

function readAdminDerivedPublicOrigin(request: Request) {
  const envBase = String(
    process.env.NEXT_PUBLIC_PUBLIC_CATALOG_BASE_URL || process.env.PUBLIC_CATALOG_BASE_URL || ""
  ).trim();
  if (envBase) return envBase.replace(/\/$/, "");

  const requestUrl = new URL(request.url);
  const forwardedHost = String(request.headers.get("x-forwarded-host") || "")
    .split(",")[0]
    .trim();
  const forwardedProto = String(request.headers.get("x-forwarded-proto") || "")
    .split(",")[0]
    .trim()
    .replace(/:$/, "");
  const host = forwardedHost || request.headers.get("host") || requestUrl.host;
  const protocol = forwardedProto || requestUrl.protocol.replace(/:$/, "") || "http";

  return `${protocol}://${host}`
    .replace(":3002", ":3001")
    .replace(/\/\/admin\./, "//")
    .replace(/\/$/, "");
}

export async function resolvePaymentLinksContext(params: {
  request: Request;
  supabase: SupabaseClient;
  user: User;
  admin?: ReturnType<typeof createAdminClient>;
}) {
  const ctx = await getCompanyContext(params.supabase, params.user);
  const admin = params.admin ?? createAdminClient();
  const ownerId = ctx.ownerId || params.user.id;
  const [{ data: profile }, { data: verifiedDomain }] = await Promise.all([
    admin.from("profiles").select("slug").eq("id", ownerId).maybeSingle(),
    admin
      .from("profile_custom_domains")
      .select("domain,verified")
      .eq("profile_id", ownerId)
      .eq("verified", true)
      .limit(1)
      .maybeSingle(),
  ]);

  const slug = normalizeText(profile?.slug);
  const verifiedCustomDomain = normalizeCustomDomain((verifiedDomain as { domain?: string | null } | null)?.domain);
  const origin = getPublicCatalogOrigin(readAdminDerivedPublicOrigin(params.request), {
    slug,
    custom_domain: verifiedCustomDomain || null,
    custom_domain_verified: Boolean(verifiedCustomDomain),
  });

  return { user: params.user, ctx, admin, ownerId, origin, slug };
}

async function generateUniqueCode(admin: ReturnType<typeof createAdminClient>) {
  for (let attempt = 0; attempt < 10; attempt += 1) {
    const code = `LP-${randomCodeSegment(6)}`;
    const { data } = await admin.from("payment_links").select("id").eq("code", code).maybeSingle();
    if (!data?.id) return code;
  }
  throw new ApiV1Error(400, "payment_link_code_failed", "Nao foi possivel gerar um codigo unico para o link.");
}

function buildLinkPayload(body: Record<string, unknown>, ownerId: string, companyId: string, slug: string) {
  const type = normalizeType(body.type);
  const status = normalizeStatus(body.status);
  const intervalCountRaw = Number(body.interval_count || 0);
  const intervalCount =
    type === "subscription" && Number.isFinite(intervalCountRaw) && intervalCountRaw > 0
      ? Math.max(1, Math.floor(intervalCountRaw))
      : null;
  const intervalUnit = type === "subscription" ? normalizeIntervalUnit(body.interval_unit) || "months" : null;
  const title = normalizeText(body.title);
  const description = normalizeText(body.description);
  const amount = normalizeMoney(body.amount);
  const currency = normalizeText(body.currency) || "BRL";
  const imageUrl = normalizeText(body.image_url) || null;
  const expiresAt = normalizeText(body.expires_at) || null;

  const checkoutConfig =
    body.checkout_config && typeof body.checkout_config === "object" && !Array.isArray(body.checkout_config)
      ? (body.checkout_config as Record<string, unknown>)
      : {};
  const shareConfig =
    body.share_config && typeof body.share_config === "object" && !Array.isArray(body.share_config)
      ? (body.share_config as Record<string, unknown>)
      : {};
  const metadata =
    body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata)
      ? (body.metadata as Record<string, unknown>)
      : {};

  if (!title) throw new ApiV1Error(400, "invalid_title", "Informe o nome do link.");
  if (amount <= 0) throw new ApiV1Error(400, "invalid_amount", "Informe um valor maior que zero.");
  if (type === "subscription" && (!intervalCount || !intervalUnit)) {
    throw new ApiV1Error(400, "invalid_recurrence", "Configure a recorrencia da assinatura.");
  }

  return {
    user_id: ownerId,
    company_id: companyId,
    slug,
    status,
    type,
    title,
    description: description || null,
    amount,
    currency,
    image_url: imageUrl,
    interval_count: intervalCount,
    interval_unit: intervalUnit,
    allow_credit_card: normalizeBoolean(body.allow_credit_card, true),
    allow_pix: type === "one_time" ? normalizeBoolean(body.allow_pix, true) : false,
    allow_boleto: type === "one_time" ? normalizeBoolean(body.allow_boleto, false) : false,
    checkout_config: checkoutConfig,
    share_config: shareConfig,
    metadata,
    expires_at: expiresAt,
    updated_at: new Date().toISOString(),
  };
}

function mapLinkRow(row: PaymentLinkRow, publicOrigin: string, subscriptions: PaymentLinkSubscriptionRow[]) {
  const linkSubscriptions = subscriptions.filter((item) => item.payment_link_id === row.id);
  const activeSubscriptionCount = linkSubscriptions.filter((item) => item.status === "authorized").length;
  const pendingSubscriptionCount = linkSubscriptions.filter((item) => item.status === "pending").length;

  return {
    id: row.id,
    code: row.code,
    slug: row.slug,
    status: row.status,
    type: row.type,
    title: row.title,
    description: row.description || "",
    amount: Number(row.amount || 0),
    currency: normalizeText(row.currency) || "BRL",
    image_url: row.image_url || "",
    interval_count: row.interval_count,
    interval_unit: row.interval_unit,
    allow_credit_card: row.allow_credit_card !== false,
    allow_pix: row.allow_pix !== false,
    allow_boleto: row.allow_boleto === true,
    checkout_config: row.checkout_config || {},
    share_config: row.share_config || {},
    metadata: row.metadata || {},
    last_paid_at: row.last_paid_at,
    last_opened_at: row.last_opened_at,
    expires_at: row.expires_at,
    created_at: row.created_at,
    updated_at: row.updated_at,
    public_url: `${publicOrigin}/lp/${encodeURIComponent(row.code)}`,
    active_subscription_count: activeSubscriptionCount,
    pending_subscription_count: pendingSubscriptionCount,
    subscriptions_total: linkSubscriptions.length,
  };
}

export async function listPaymentLinks(params: { resolved: PaymentLinkRequestContext }) {
  const { user, ctx, admin, ownerId, origin } = params.resolved;
  if (!canCompany(ctx, user.id, "financial.links.read")) {
    throw new ApiV1Error(403, "forbidden", "Sem permissao para visualizar links de pagamento.");
  }

  const [{ data: links, error: linksError }, { data: subscriptions, error: subscriptionsError }] = await Promise.all([
    admin.from("payment_links").select("*").eq("user_id", ownerId).order("created_at", { ascending: false }),
    admin
      .from("payment_link_subscriptions")
      .select("id,payment_link_id,status,amount,created_at")
      .eq("user_id", ownerId)
      .order("created_at", { ascending: false }),
  ]);

  if (linksError) throw new ApiV1Error(400, "payment_links_fetch_failed", linksError.message);
  if (subscriptionsError) throw new ApiV1Error(400, "payment_link_subscriptions_fetch_failed", subscriptionsError.message);

  return {
    ok: true,
    links: (links || []).map((row) => mapLinkRow(row as PaymentLinkRow, origin, (subscriptions || []) as PaymentLinkSubscriptionRow[])),
  };
}

export async function getPaymentLinkById(params: { resolved: PaymentLinkRequestContext; id: string }) {
  const list = await listPaymentLinks({ resolved: params.resolved });
  const link = list.links.find((item) => item.id === params.id);
  if (!link) throw new ApiV1Error(404, "payment_link_not_found", "Link de pagamento nao encontrado.");
  return { ok: true, link };
}

export async function createPaymentLink(params: {
  resolved: PaymentLinkRequestContext;
  body: Record<string, unknown> | null;
}) {
  const { user, ctx, admin, ownerId, slug } = params.resolved;
  if (!canCompany(ctx, user.id, "financial.links.write")) {
    throw new ApiV1Error(403, "forbidden", "Sem permissao para criar links de pagamento.");
  }

  const payload = buildLinkPayload(params.body || {}, ownerId, ctx.companyId, slug);
  const code = await generateUniqueCode(admin);

  const { data, error } = await admin
    .from("payment_links")
    .insert({
      ...payload,
      code,
    })
    .select("*")
    .single();

  if (error || !data) throw new ApiV1Error(400, "payment_link_create_failed", error?.message || "Nao foi possivel criar o link.");
  return { ok: true, link: data };
}

export async function updatePaymentLink(params: {
  resolved: PaymentLinkRequestContext;
  id: string;
  body: Record<string, unknown> | null;
}) {
  const { user, ctx, admin, ownerId, slug } = params.resolved;
  if (!canCompany(ctx, user.id, "financial.links.write")) {
    throw new ApiV1Error(403, "forbidden", "Sem permissao para editar links de pagamento.");
  }

  const payload = buildLinkPayload(params.body || {}, ownerId, ctx.companyId, slug);
  const { data, error } = await admin
    .from("payment_links")
    .update(payload)
    .eq("id", params.id)
    .eq("user_id", ownerId)
    .select("*")
    .single();

  if (error || !data) throw new ApiV1Error(400, "payment_link_update_failed", error?.message || "Nao foi possivel atualizar o link.");
  return { ok: true, link: data };
}

export async function deletePaymentLink(params: { resolved: PaymentLinkRequestContext; id: string }) {
  const { user, ctx, admin, ownerId } = params.resolved;
  if (!canCompany(ctx, user.id, "financial.links.write")) {
    throw new ApiV1Error(403, "forbidden", "Sem permissao para excluir links de pagamento.");
  }

  const { error } = await admin.from("payment_links").delete().eq("id", params.id).eq("user_id", ownerId);
  if (error) throw new ApiV1Error(400, "payment_link_delete_failed", error.message);
  return { ok: true };
}
