/**
 * POST /api/rank-tracking/keywords — bulk add keywords to track
 * GET  /api/rank-tracking/keywords?brandId=X — list active keywords
 */

import { NextResponse } from "next/server";
import { getCurrentUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { backfillKeyword } from "@/lib/services/rank-tracker-backfill";

export const dynamic = "force-dynamic";

// ── Auth helper ──────────────────────────────────────────────────────────────
async function authBrand(brandId: string, userId: string) {
  const brand = await prisma.brand.findUnique({
    where: { id: brandId },
    include: { organization: { include: { memberships: true } } },
  });
  if (!brand) return { error: "Brand not found" as const, status: 404 };
  if (!brand.organization.memberships.some((m) => m.userId === userId)) {
    return { error: "Access denied" as const, status: 403 };
  }
  return { brand };
}

// ── POST: Bulk add keywords ──────────────────────────────────────────────────

interface KeywordInput {
  keyword: string;
  location?: string;
  device?: string;
  targetUrl?: string;
  tags?: string[];
}

export async function POST(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const body = await req.json().catch(() => null) as { brandId?: string; keywords?: KeywordInput[] } | null;
  if (!body?.brandId || !Array.isArray(body.keywords) || body.keywords.length === 0) {
    return NextResponse.json({ error: "brandId and keywords[] required" }, { status: 400 });
  }

  const resolved = await authBrand(body.brandId, user.id);
  if ("error" in resolved) return NextResponse.json({ error: resolved.error }, { status: resolved.status });

  // Dedupe: fetch existing (keyword, location, device) combos for this brand.
  const existing = await prisma.trackedKeyword.findMany({
    where: { brandId: body.brandId },
    select: { keyword: true, location: true, device: true },
  });
  const existingSet = new Set(existing.map((e) => `${e.keyword}||${e.location}||${e.device}`));

  const toCreate = body.keywords
    .filter((k) => k.keyword?.trim())
    .map((k) => ({
      keyword: k.keyword.trim(),
      location: k.location || "United States",
      device: k.device === "mobile" ? "mobile" : "desktop",
      targetUrl: k.targetUrl || null,
      tags: Array.isArray(k.tags) ? k.tags : [],
    }))
    .filter((k) => !existingSet.has(`${k.keyword}||${k.location}||${k.device}`));

  if (toCreate.length > 0) {
    await prisma.trackedKeyword.createMany({
      data: toCreate.map((k) => ({ brandId: body.brandId!, ...k })),
      skipDuplicates: true,
    });

    // Fire-and-forget GSC backfill for the newly-created keywords so the
    // position chart isn't empty for the first 90 days of tracking. We
    // re-query for the created rows (createMany doesn't return them) and
    // fan out one backfill per keyword. Intentionally not awaited — the
    // client doesn't need to block on a historical backfill to render
    // its optimistic list.
    const createdKeys = new Set(toCreate.map((k) => `${k.keyword}||${k.location}||${k.device}`));
    prisma.trackedKeyword.findMany({
      where: { brandId: body.brandId },
      select: { id: true, keyword: true, location: true, device: true },
    }).then((all) => {
      const newRows = all.filter((r) => createdKeys.has(`${r.keyword}||${r.location}||${r.device}`));
      for (const kw of newRows) {
        backfillKeyword(body.brandId!, kw.id).catch((err) => {
          console.error(`[rank-tracking] backfill failed for ${kw.id}:`, err instanceof Error ? err.message : err);
        });
      }
    }).catch((err) => {
      console.error("[rank-tracking] post-create keyword lookup failed:", err instanceof Error ? err.message : err);
    });
  }

  return NextResponse.json({ created: toCreate.length, duplicatesSkipped: body.keywords.length - toCreate.length });
}

// ── GET: List active keywords with latest ranking ────────────────────────────

export async function GET(req: Request) {
  const user = await getCurrentUser();
  if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

  const { searchParams } = new URL(req.url);
  const brandId = searchParams.get("brandId");
  if (!brandId) return NextResponse.json({ error: "brandId required" }, { status: 400 });

  const resolved = await authBrand(brandId, user.id);
  if ("error" in resolved) return NextResponse.json({ error: resolved.error }, { status: resolved.status });

  const keywords = await prisma.trackedKeyword.findMany({
    where: { brandId, isActive: true },
    include: {
      rankings: { orderBy: { checkedAt: "desc" }, take: 1 },
      _count: { select: { rankings: true } },
    },
    orderBy: { createdAt: "asc" },
  });

  // For "best position ever" we do a single raw aggregate per keyword
  const bestPositions = await prisma.keywordRanking.groupBy({
    by: ["trackedKeywordId"],
    _min: { position: true },
    where: { trackedKeywordId: { in: keywords.map((k) => k.id) }, position: { not: null } },
  });
  const bestMap = new Map(bestPositions.map((b) => [b.trackedKeywordId, b._min.position]));

  const result = keywords.map((kw) => {
    const latest = kw.rankings[0] ?? null;
    const change = latest?.position != null && latest.previousPosition != null
      ? latest.previousPosition - latest.position // positive = improved
      : null;
    // Defensive parse for the JSON competitors blob — older rows
    // pre-DataForSEO predate the column and serialize to []. Newer
    // DataForSEO rows are arrays of {domain, position, url, title}.
    const rawCompetitors = (latest?.competitors as unknown);
    const competitors = Array.isArray(rawCompetitors) ? rawCompetitors : [];
    return {
      id: kw.id,
      keyword: kw.keyword,
      location: kw.location,
      device: kw.device,
      targetUrl: kw.targetUrl,
      tags: kw.tags,
      createdAt: kw.createdAt,
      daysTracked: kw._count.rankings,
      currentPosition: latest?.position ?? null,
      previousPosition: latest?.previousPosition ?? null,
      change,
      url: latest?.url ?? null,
      bestPosition: bestMap.get(kw.id) ?? null,
      lastChecked: latest?.checkedAt ?? null,
      // SERP features detected on the most recent check (DataForSEO only).
      // Empty array for keywords backfilled from GSC or checked via Google CSE.
      serpFeatures: latest?.serpFeatures ?? [],
      // Top organic competitors on the same SERP, captured at the most
      // recent check. Surfaced inside the expandable row in the UI.
      competitors,
    };
  });

  return NextResponse.json({ keywords: result });
}
