import { env } from "../../core/env.js";

type GdsMovieItem = {
  name: string;
  path: string;
  stream_url?: string;
  mtime?: string;
  is_dir?: boolean;
  source_id?: string | number;
  meta_poster?: string;
  poster?: string;
  thumb?: string;
};

type GdsMovieSearchResponse = {
  ret?: string;
  count?: number;
  list?: GdsMovieItem[];
};

function normalizeBaseUrl(baseUrl: string): string {
  return baseUrl.replace(/\/+$/, "");
}

function stripOuterQuotes(input: string): string {
  const t = String(input || "").trim();
  if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
    return t.slice(1, -1).trim();
  }
  return t;
}

function normalizeForMatch(input: string): string {
  return String(input || "")
    .toLowerCase()
    .replace(/[\[\]\(\)\{\}\-_.:]/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

function scoreItem(query: string, item: GdsMovieItem): number {
  const q = normalizeForMatch(query);
  const name = normalizeForMatch(item.name || "");
  const path = normalizeForMatch(item.path || "");
  const compactQ = q.replace(/\s+/g, "");
  const compactName = name.replace(/\s+/g, "");
  const compactPath = path.replace(/\s+/g, "");
  const tokens = q.split(" ").filter(Boolean);

  let score = 0;
  if (compactQ && compactName.includes(compactQ)) score += 120;
  if (compactQ && compactPath.includes(compactQ)) score += 60;
  for (const t of tokens) {
    if (name.includes(t)) score += 18;
    if (path.includes(t)) score += 7;
  }
  if (!item.is_dir) score += 5;
  return score;
}

async function fetchSearch(
  base: string,
  apiKey: string,
  query: string,
  sourceId: string,
  limit: number,
  parentPath: string | null,
  isDir: boolean,
): Promise<GdsMovieItem[]> {
  const url = new URL(`${base}/search`);
  url.searchParams.set("apikey", apiKey);
  url.searchParams.set("query", query);
  if (parentPath) url.searchParams.set("parent_path", parentPath);
  url.searchParams.set("recursive", "true");
  url.searchParams.set("limit", String(limit));
  url.searchParams.set("offset", "0");
  url.searchParams.set("is_dir", isDir ? "true" : "false");
  url.searchParams.set("search_in_path", "true");
  url.searchParams.set("sort_by", "date");
  url.searchParams.set("sort_order", "desc");
  url.searchParams.set("video_only", "true");
  url.searchParams.set("source_id", sourceId);

  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), 12000);
  try {
    const response = await fetch(url.toString(), { signal: ctrl.signal });
    if (!response.ok) return [];
    const payload = (await response.json()) as GdsMovieSearchResponse;
    if (payload?.ret !== "success") return [];
    const list = Array.isArray(payload.list) ? payload.list : [];
    return list.filter((item) => Boolean(item?.path));
  } finally {
    clearTimeout(timer);
  }
}

export const gdsMovieService = {
  async searchByTitle(rawQuery: string, limit = 5): Promise<GdsMovieItem[]> {
    const query = stripOuterQuotes(rawQuery);
    if (!query) throw new Error("검색어가 비었습니다.");
    if (!env.GDS_DVIEWER_BASE_URL || !env.GDS_DVIEWER_API_KEY) {
      throw new Error("GDS_DVIEWER_BASE_URL / GDS_DVIEWER_API_KEY 설정이 필요합니다.");
    }

    const base = normalizeBaseUrl(env.GDS_DVIEWER_BASE_URL);
    const apiKey = env.GDS_DVIEWER_API_KEY;
    const max = Math.max(1, Math.min(limit, 10));
    const fetchLimit = Math.max(20, Math.min(100, max * 8));
    const parentCandidates: Array<string | null> = [
      "VIDEO/영화",
      "VIDEO/영화/최신",
      "VIDEO/영화/제목",
      "VIDEO/영화/UHD",
      "VIDEO",
      null,
    ];

    const seen = new Set<string>();
    const merged: GdsMovieItem[] = [];
    for (const parentPath of parentCandidates) {
      for (const isDir of [false, true]) {
        const rows = await fetchSearch(
          base,
          apiKey,
          query,
          env.GDS_DVIEWER_SOURCE_ID,
          fetchLimit,
          parentPath,
          isDir,
        );
        for (const row of rows) {
          if (!seen.has(row.path)) {
            seen.add(row.path);
            merged.push(row);
          }
        }
      }
    }
    return merged
      .map((item) => ({ item, score: scoreItem(query, item) }))
      .sort((a, b) => b.score - a.score)
      .filter((row) => row.score > 0)
      .slice(0, max)
      .map((row) => row.item);
  },
};

export type { GdsMovieItem };
