import { NextResponse } from "next/server";
import type { NpmSearchResult, NpmRegistrySearchResponse } from "@/lib/package-types";

export const dynamic = "force-dynamic";

const NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
const DEFAULT_SIZE = 30;
const MAX_SIZE = 100;
const MIN_SIZE = 1;

function parseSize(value: unknown): number {
  const n = typeof value === "number" ? value : Number(value);
  if (!Number.isFinite(n)) return DEFAULT_SIZE;
  return Math.min(MAX_SIZE, Math.max(MIN_SIZE, Math.floor(n)));
}

/** Normalize npm repository URL to a clean HTTPS URL the browser can open. */
function cleanRepoUrl(url: string | undefined): string | undefined {
  if (!url) return undefined;
  // git+https://github.com/owner/repo.git → https://github.com/owner/repo
  // git+ssh://git@github.com/owner/repo.git → https://github.com/owner/repo
  let cleaned = url.replace(/^git\+(https?:\/\/)/, "$1");
  cleaned = cleaned.replace(/^git\+ssh:\/\/git@([^/]+)\/(.*)/, (_, host, path) => {
    return `https://${host}/${path}`;
  });
  cleaned = cleaned.replace(/\.git$/, "");
  // Remove trailing slash
  cleaned = cleaned.replace(/\/$/, "");
  return cleaned || undefined;
}

/** Infer resource types from npm keywords. */
function inferResourceTypes(keywords: string[]): NpmSearchResult["resourceTypes"] {
  const types: NpmSearchResult["resourceTypes"] = [];
  const kw = new Set(keywords.map((k) => k.toLowerCase()));
  if (kw.has("extension") || kw.has("pi-extension")) types.push("extension");
  if (kw.has("skill") || kw.has("pi-skill")) types.push("skill");
  if (kw.has("prompt") || kw.has("pi-prompt") || kw.has("prompt-template")) types.push("prompt");
  if (kw.has("theme") || kw.has("pi-theme")) types.push("theme");
  // Fallback: anything tagged pi-package but no specific type → infer from name
  if (types.length === 0 && kw.has("pi-package")) {
    const name = [...kw].find((k) => k.startsWith("pi-") && k !== "pi-package" && !k.startsWith("pi-coding-agent"));
    if (name === "pi-extension") types.push("extension");
    else if (name === "pi-skill") types.push("skill");
    else if (name === "pi-prompt") types.push("prompt");
    else if (name === "pi-theme") types.push("theme");
    else types.push("extension"); // most pi-* packages are extensions
  }
  return types;
}

// POST /api/packages/search
// body: { query?: string; size?: number }
export async function POST(req: Request) {
  try {
    const body = await req.json().catch(() => ({})) as { query?: string; size?: unknown };
    const { query } = body;
    const size = parseSize(body.size);

    // Build npm search query
    // Always includes keywords:pi-package; optionally filter by additional terms
    let searchQuery = "keywords:pi-package";
    if (query?.trim()) {
      // Escape special chars and add as a text search term
      // The npm search API uses Lucene syntax; we keep it simple
      const cleaned = query.trim().replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, " ");
      if (cleaned) searchQuery += ` ${encodeURIComponent(cleaned)}`;
    }

    const url = `${NPM_SEARCH_URL}?text=${searchQuery}&size=${size}`;
    const res = await fetch(url, {
      cache: "no-store",
      headers: { Accept: "application/json" },
    });

    if (!res.ok) {
      return NextResponse.json(
        { error: `npm registry search failed: HTTP ${res.status}` },
        { status: 502 },
      );
    }

    const data = (await res.json()) as NpmRegistrySearchResponse;

    const results: NpmSearchResult[] = (data.objects ?? []).map((obj) => {
      const p = obj.package;
      const keywords = p.keywords ?? [];

      // Derive a simple resource-types label from keywords
      const resourceTypes = inferResourceTypes(keywords);

      return {
        name: p.name,
        version: p.version,
        description: p.description?.replace(/<[^>]+>/g, "").trim() ?? "",
        downloads: obj.downloads.monthly,
        publisher: p.publisher?.username ?? "unknown",
        links: {
          npm: p.links.npm,
          homepage: p.links.homepage,
          repository: cleanRepoUrl(p.links.repository) ?? p.links.homepage,
        },
        date: p.date,
        keywords,
        resourceTypes,
      };
    });

    return NextResponse.json({ results, total: data.total });
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : String(e);
    return NextResponse.json({ error: msg }, { status: 500 });
  }
}
