/**
 * GET /api/backlinks/disavow-file?brandId=X — Google Disavow format file.
 *
 * The generated .txt file clusters disavowed backlinks into
 * domain-level entries when 3+ links share a source domain; per-URL
 * entries otherwise (Google's recommended format).
 *
 * Each entry is preceded by a comment block documenting the highest
 * toxicity score among the disavowed backlinks from that source and
 * the union of reasons the scorer recorded. Comments are ignored by
 * Google's disavow tool but give the user (and any future auditor) a
 * plain-English record of *why* a given domain was disavowed — useful
 * if the file is ever re-uploaded or handed off to another team.
 */
import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { scoreToTier } from "@/lib/services/backlink-toxicity";

export const dynamic = "force-dynamic";

interface DisavowedRow {
  sourceDomain: string;
  sourceUrl: string;
  toxicityScore: number | null;
  toxicityReasons: string[];
}

function commentBlock(score: number | null, reasons: string[]): string[] {
  const lines: string[] = [];
  if (score != null) {
    const tier = scoreToTier(score);
    lines.push(`#   Toxicity: ${score} (${tier})`);
  }
  if (reasons.length > 0) {
    // Dedup + join. Multiple links from the same source can surface
    // identical reasons; collapsing keeps the comment block tight.
    const unique = Array.from(new Set(reasons));
    lines.push(`#   Reasons: ${unique.join("; ")}`);
  }
  return lines;
}

export async function GET(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  const brandId = new URL(req.url).searchParams.get("brandId");
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  const brand = await prisma.brand.findUnique({ where: { id: brandId }, include: { organization: { include: { memberships: true } } } });
  if (!brand) return NextResponse.json({ error: "Brand not found" }, { status: 404 });
  if (!brand.organization.memberships.some((m) => m.userId === user.id)) return NextResponse.json({ error: "Access denied" }, { status: 403 });

  const disavowed = await prisma.backlink.findMany({
    where: { brandId, status: "disavowed" },
    select: { sourceDomain: true, sourceUrl: true, toxicityScore: true, toxicityReasons: true },
  });

  // Bucket into domain-level vs URL-level entries. 3+ links from a
  // single source → collapse to `domain:` (Google's recommended form).
  const countBySourceDomain = new Map<string, number>();
  for (const d of disavowed) {
    countBySourceDomain.set(d.sourceDomain, (countBySourceDomain.get(d.sourceDomain) ?? 0) + 1);
  }

  const domainGroups = new Map<string, DisavowedRow[]>();
  const urlRows: DisavowedRow[] = [];
  for (const row of disavowed as DisavowedRow[]) {
    if ((countBySourceDomain.get(row.sourceDomain) ?? 0) >= 3) {
      const arr = domainGroups.get(row.sourceDomain) ?? [];
      arr.push(row);
      domainGroups.set(row.sourceDomain, arr);
    } else {
      urlRows.push(row);
    }
  }

  const totalEntries = domainGroups.size + urlRows.length;

  const lines: string[] = [
    `# Disavow file generated by meSEO`,
    `# Brand: ${brand.name}`,
    `# Domain: ${brand.domain}`,
    `# Generated: ${new Date().toISOString()}`,
    `# Total entries: ${totalEntries} (${domainGroups.size} domain-level + ${urlRows.length} URL-level)`,
    `#`,
    `# Upload this file to Google Search Console:`,
    `#   https://search.google.com/search-console/disavow-links`,
    ``,
  ];

  // Domain-level entries — sort for deterministic output + diff-ability.
  for (const domain of [...domainGroups.keys()].sort()) {
    const rows = domainGroups.get(domain)!;
    // Highest score across the grouped links drives the comment tier —
    // the domain is no better than its worst link.
    const maxScore = rows.reduce<number | null>((acc, r) => {
      if (r.toxicityScore == null) return acc;
      return acc == null ? r.toxicityScore : Math.max(acc, r.toxicityScore);
    }, null);
    const allReasons = rows.flatMap((r) => r.toxicityReasons ?? []);
    lines.push(`# ${domain}`);
    lines.push(...commentBlock(maxScore, allReasons));
    lines.push(`domain:${domain}`);
    lines.push(``);
  }

  // URL-level entries — same treatment, one comment block per URL.
  for (const row of urlRows.sort((a, b) => a.sourceUrl.localeCompare(b.sourceUrl))) {
    lines.push(`# ${row.sourceUrl}`);
    lines.push(...commentBlock(row.toxicityScore, row.toxicityReasons ?? []));
    lines.push(row.sourceUrl);
    lines.push(``);
  }

  return new NextResponse(lines.join("\n"), {
    headers: {
      "Content-Type": "text/plain; charset=utf-8",
      "Content-Disposition": `attachment; filename="disavow-${brand.domain}-${new Date().toISOString().split("T")[0]}.txt"`,
    },
  });
}
