import { promises as fs } from "node:fs";
import path from "node:path";

export type BlogPostMeta = {
  slug: string;
  title: string;
  date: string; // YYYY-MM-DD (date-only)
  excerpt: string;
  coverImage?: string;
  tags?: string[];
  readingTimeMinutes?: number;
};

export type BlogPost = BlogPostMeta & {
  content: string;
};

const POSTS_DIR = path.join(process.cwd(), "content", "blog", "posts");

function parseFrontmatter(raw: string): { data: Record<string, string>; body: string } {
  const trimmed = raw.replace(/\r\n/g, "\n");
  if (!trimmed.startsWith("---\n")) return { data: {}, body: trimmed };

  let end = trimmed.indexOf("\n---\n", 4);
  let bodyStart = end === -1 ? -1 : end + "\n---\n".length;
  if (end === -1) {
    // Tolerate a missing newline after the closing delimiter (e.g. accidental "---" + body concatenation),
    // but still require the delimiter to be exactly 3 dashes (not "------").
    let close = trimmed.indexOf("\n---", 4);
    while (close !== -1 && trimmed[close + 4] === "-") {
      close = trimmed.indexOf("\n---", close + 4);
    }
    if (close === -1) return { data: {}, body: trimmed };
    end = close;
    bodyStart = close + "\n---".length;
    if (trimmed[bodyStart] === "\n") bodyStart += 1;
  }

  const fm = trimmed.slice(4, end).trim();
  const body = trimmed.slice(bodyStart);

  const data: Record<string, string> = {};
  for (const line of fm.split("\n")) {
    const l = line.trim();
    if (!l || l.startsWith("#")) continue;
    const idx = l.indexOf(":");
    if (idx === -1) continue;
    const key = l.slice(0, idx).trim();
    const value = l.slice(idx + 1).trim();
    if (!key) continue;
    data[key] = value;
  }
  return { data, body };
}

function normalizeDate(input: string | undefined): string {
  if (!input) return "1970-01-01";
  const raw = input.trim();
  if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) return raw;
  const d = new Date(raw);
  if (Number.isNaN(d.getTime())) return "1970-01-01";
  // Canonicalize to date-only to avoid timezone shifts in UI.
  return d.toISOString().slice(0, 10);
}

function estimateReadingTimeMinutes(text: string): number | undefined {
  const words = text
    .replace(/```[\s\S]*?```/g, " ")
    .replace(/[`*_#[\\]()>-]/g, " ")
    .split(/\s+/)
    .filter(Boolean).length;
  if (!words) return undefined;
  return Math.max(1, Math.round(words / 220));
}

function parseTags(tagsRaw: string | undefined): string[] | undefined {
  if (!tagsRaw) return undefined;
  const tags = tagsRaw
    .split(",")
    .map((t) => t.trim())
    .filter(Boolean);
  return tags.length ? tags : undefined;
}

async function readPostFile(slug: string): Promise<BlogPost | null> {
  const file = path.join(POSTS_DIR, `${slug}.md`);
  let raw: string;
  try {
    raw = await fs.readFile(file, "utf8");
  } catch {
    return null;
  }

  const { data, body } = parseFrontmatter(raw);
  const title = data.title || slug;
  const excerpt = data.excerpt || body.trim().split("\n").find(Boolean) || "";
  const coverImage = data.coverImage || undefined;
  const date = normalizeDate(data.date);
  const tags = parseTags(data.tags);
  const readingTimeMinutes = estimateReadingTimeMinutes(body);

  return {
    slug,
    title,
    date,
    excerpt,
    coverImage,
    tags,
    readingTimeMinutes,
    content: body.trim(),
  };
}

export async function getAllBlogSlugs(): Promise<string[]> {
  let entries: string[] = [];
  try {
    entries = await fs.readdir(POSTS_DIR);
  } catch {
    return [];
  }
  return entries
    .filter((name) => name.endsWith(".md"))
    .map((name) => name.slice(0, -3))
    .sort();
}

export async function getAllBlogPosts(opts?: { limit?: number }): Promise<BlogPostMeta[]> {
  const slugs = await getAllBlogSlugs();
  const posts = (await Promise.all(slugs.map((s) => readPostFile(s)))).filter(Boolean) as BlogPost[];
  posts.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));
  const metas = posts.map(({ content: _content, ...meta }) => meta);
  if (opts?.limit) return metas.slice(0, opts.limit);
  return metas;
}

export async function getBlogPost(slug: string): Promise<BlogPost | null> {
  return readPostFile(slug);
}
