/**
 * URL Content Extractor — server-side only
 *
 * Supports:
 *   - Website/blog posts (generic HTML → text extraction)
 *   - Facebook posts (og:description / article meta)
 *   - Instagram posts (og:description / caption)
 *   - Twitter/X posts (og:description / tweet meta)
 *
 * Strategy:
 *   1. Fetch the URL with proper User-Agent
 *   2. Extract Open Graph meta tags (og:title, og:description, og:image)
 *   3. Extract main article/post text body
 *   4. Detect platform and apply platform-specific extraction
 *   5. Return structured content for AI processing
 */

export interface ExtractedContent {
  url: string;
  platform: "website" | "facebook" | "instagram" | "twitter" | "unknown";
  title: string;
  description: string;
  body: string;
  author?: string;
  imageUrl?: string;
  publishedAt?: string;
  rawLength: number;
}

/** Detect social platform from URL */
function detectPlatform(url: string): ExtractedContent["platform"] {
  const u = url.toLowerCase();
  if (u.includes("facebook.com") || u.includes("fb.com")) return "facebook";
  if (u.includes("instagram.com")) return "instagram";
  if (u.includes("twitter.com") || u.includes("x.com")) return "twitter";
  return "website";
}

/** Extract content between two tag markers — lightweight, no DOM parser needed */
function extractBetween(html: string, open: string, close: string): string {
  const start = html.indexOf(open);
  if (start === -1) return "";
  const contentStart = start + open.length;
  const end = html.indexOf(close, contentStart);
  if (end === -1) return "";
  return html.slice(contentStart, end).trim();
}

/** Extract meta tag content by property or name */
function extractMeta(html: string, attr: string, value: string): string {
  // Match <meta property="og:title" content="..."> or <meta name="..." content="...">
  const patterns = [
    new RegExp(`<meta[^>]+${attr}=["']${value}["'][^>]+content=["']([^"']+)["']`, "i"),
    new RegExp(`<meta[^>]+content=["']([^"']+)["'][^>]+${attr}=["']${value}["']`, "i"),
  ];
  for (const re of patterns) {
    const m = html.match(re);
    if (m?.[1]) return decodeHtmlEntities(m[1].trim());
  }
  return "";
}

/** Decode basic HTML entities */
function decodeHtmlEntities(text: string): string {
  return text
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&nbsp;/g, " ")
    .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n)));
}

/** Strip HTML tags from a string */
function stripTags(html: string): string {
  return html
    .replace(/<script[\s\S]*?<\/script>/gi, " ")
    .replace(/<style[\s\S]*?<\/style>/gi, " ")
    .replace(/<[^>]+>/g, " ")
    .replace(/\s{2,}/g, " ")
    .trim();
}

/** Extract the main body text from HTML */
function extractBody(html: string): string {
  // Strategi: coba elemen semantik terlebih dahulu (lebih reliable)
  const candidates = [
    // Elemen semantik HTML5
    { open: "<article",  close: "</article>"  },
    { open: "<main",     close: "</main>"     },
    // Selectors class umum — cari mana yang lebih panjang
    { open: 'class="post-content',   close: "</div>" },
    { open: 'class="entry-content',  close: "</div>" },
    { open: 'class="article-body',   close: "</div>" },
    { open: 'class="content-body',   close: "</div>" },
    { open: 'class="post-body',      close: "</div>" },
  ];

  let best = "";
  for (const { open, close } of candidates) {
    const startIdx = html.indexOf(open);
    if (startIdx === -1) continue;

    // Untuk div, kita perlu menemukan closing tag yang tepat (level-aware)
    // karena extractBetween yang naive akan stop di </div> pertama yang ditemukan.
    let extracted = "";
    if (close === "</div>") {
      extracted = extractDivContent(html, startIdx);
    } else {
      extracted = extractBetween(html, open, close);
    }

    if (extracted.length > best.length) {
      best = extracted;
    }
  }

  if (!best) {
    // Fallback: kumpulkan semua <p> tags dengan konten meaningful
    const paragraphs = Array.from(html.matchAll(/<p[^>]*>([\s\S]*?)<\/p>/gi))
      .map((m) => stripTags(m[1]))
      .filter((t) => t.length > 40);
    best = paragraphs.slice(0, 20).join("\n\n");
  }

  return stripTags(best).slice(0, 8000); // cap at 8k chars
}

/**
 * Extract content from a div element with proper nesting depth tracking.
 * Avoids the problem of stopping at the first </div> regardless of nesting level.
 */
function extractDivContent(html: string, startIdx: number): string {
  let depth = 0;
  let i = startIdx;
  const len = html.length;

  while (i < len) {
    if (html.slice(i, i + 4).toLowerCase() === "<div") {
      depth++;
      i += 4;
    } else if (html.slice(i, i + 6).toLowerCase() === "</div>") {
      depth--;
      if (depth <= 0) {
        return html.slice(startIdx, i + 6);
      }
      i += 6;
    } else {
      i++;
    }
  }
  return html.slice(startIdx); // unterminated — return rest of string
}

/** Main extractor function */
export async function extractUrlContent(url: string): Promise<ExtractedContent> {
  const platform = detectPlatform(url);

  // Normalize URL
  let fetchUrl = url.trim();
  if (!fetchUrl.startsWith("http")) fetchUrl = `https://${fetchUrl}`;

  // ── SSRF protection ────────────────────────────────────────────────────────
  // Tolak URL yang mengarah ke jaringan internal / localhost
  let parsedUrl: URL;
  try {
    parsedUrl = new URL(fetchUrl);
  } catch {
    throw new Error("URL tidak valid. Pastikan format URL benar (contoh: https://example.com/post).");
  }

  if (parsedUrl.protocol !== "https:" && parsedUrl.protocol !== "http:") {
    throw new Error("Hanya URL dengan protokol http/https yang diizinkan.");
  }

  const hostname = parsedUrl.hostname.toLowerCase();
  const BLOCKED_HOSTS = [
    "localhost",
    "127.0.0.1",
    "0.0.0.0",
    "::1",
    "[::1]",
  ];
  if (BLOCKED_HOSTS.includes(hostname)) {
    throw new Error("URL tidak diizinkan: alamat loopback diblokir.");
  }

  // Blokir private IP ranges (10.x, 172.16-31.x, 192.168.x)
  const privateIpPattern =
    /^(10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|169\.254\.\d+\.\d+|fc[0-9a-f]{2}:|fd[0-9a-f]{2}:)/i;
  if (privateIpPattern.test(hostname)) {
    throw new Error("URL tidak diizinkan: rentang IP privat diblokir.");
  }

  const res = await fetch(fetchUrl, {
    headers: {
      "User-Agent":
        "Mozilla/5.0 (compatible; PromptMotionBot/1.0; +https://promptmotion.ai)",
      "Accept": "text/html,application/xhtml+xml",
      "Accept-Language": "id,en;q=0.9",
    },
    signal: AbortSignal.timeout(10000), // 10s timeout
    redirect: "follow",
  });

  if (!res.ok) {
    throw new Error(`Gagal mengambil konten dari URL (HTTP ${res.status}). Pastikan URL valid dan dapat diakses publik.`);
  }

  const html = await res.text();

  // Extract Open Graph + meta tags
  const ogTitle       = extractMeta(html, "property", "og:title");
  const ogDescription = extractMeta(html, "property", "og:description");
  const ogImage       = extractMeta(html, "property", "og:image");
  const ogAuthor      = extractMeta(html, "property", "article:author") || extractMeta(html, "name", "author");
  const ogPublished   = extractMeta(html, "property", "article:published_time");

  // Title fallback: <title> tag
  const titleTag = extractBetween(html, "<title>", "</title>");
  const title = ogTitle || decodeHtmlEntities(titleTag) || "Untitled";

  // Description fallback: meta description
  const metaDesc = extractMeta(html, "name", "description");
  const description = ogDescription || metaDesc || "";

  // Body text
  const body = extractBody(html);

  // Twitter/X: tweet text is often in og:description
  // Instagram: caption is in og:description
  // Facebook: post text in og:description

  const combinedText = [description, body].filter(Boolean).join("\n\n");

  return {
    url: fetchUrl,
    platform,
    title,
    description,
    body: combinedText.slice(0, 8000),
    author: ogAuthor || undefined,
    imageUrl: ogImage || undefined,
    publishedAt: ogPublished || undefined,
    rawLength: html.length,
  };
}
