import "server-only";

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

import type { BlogPost, BlogPostMeta } from "@/lib/blog";

export type AdminBlogPostUpsert = {
  slug: string;
  title: string;
  date: string; // YYYY-MM-DD or ISO
  excerpt: string;
  coverImage?: string;
  tags?: string; // comma-separated
  content: string;
};

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

function normalizeSlug(input: string): string {
  const s = input
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
  if (!s) throw new Error("Invalid slug.");
  if (s.includes("..")) throw new Error("Invalid slug.");
  return s;
}

function todayLocalYYYYMMDD(): string {
  const d = new Date();
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

function normalizeDateForFrontmatter(input: string): string {
  const raw = input.trim();
  if (!raw) return todayLocalYYYYMMDD();
  if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) return raw;
  const d = new Date(raw);
  if (Number.isNaN(d.getTime())) throw new Error("Invalid date.");
  return d.toISOString().slice(0, 10);
}

function safeFileName(name: string): string {
  const cleaned = name
    .trim()
    .replace(/[/\\]+/g, "-")
    .replace(/\s+/g, "-")
    .replace(/[^a-zA-Z0-9._-]/g, "");
  if (!cleaned) throw new Error("Invalid filename.");
  if (cleaned.includes("..")) throw new Error("Invalid filename.");
  return cleaned;
}

function validateExistingImageFileName(fileName: string): string {
  const trimmed = fileName.trim();
  if (!trimmed) throw new Error("Missing filename.");
  if (trimmed.includes("..")) throw new Error("Invalid filename.");
  if (trimmed.includes("/") || trimmed.includes("\\")) throw new Error("Invalid filename.");
  if (path.basename(trimmed) !== trimmed) throw new Error("Invalid filename.");

  const lower = trimmed.toLowerCase();
  const ok =
    lower.endsWith(".png") ||
    lower.endsWith(".jpg") ||
    lower.endsWith(".jpeg") ||
    lower.endsWith(".webp") ||
    lower.endsWith(".gif") ||
    lower.endsWith(".svg");
  if (!ok) throw new Error("Invalid image type.");

  return trimmed;
}

function renderFrontmatter(p: AdminBlogPostUpsert): string {
  const title = (p.title || "").replace(/\r?\n/g, " ").trim() || p.slug;
  const excerpt = (p.excerpt || "").replace(/\r?\n/g, " ").trim();
  const date = normalizeDateForFrontmatter(p.date);
  const coverImage = (p.coverImage || "").trim();
  const tags = (p.tags || "").replace(/\r?\n/g, " ").trim();

  const lines: string[] = [];
  lines.push("---");
  lines.push(`title: ${title}`);
  lines.push(`date: ${date}`);
  if (excerpt) lines.push(`excerpt: ${excerpt}`);
  if (coverImage) lines.push(`coverImage: ${coverImage}`);
  if (tags) lines.push(`tags: ${tags}`);
  lines.push("---");
  return lines.join("\n") + "\n\n";
}

function stripLeadingFrontmatterBlocks(markdown: string): string {
  let s = (markdown || "").replace(/\r\n/g, "\n");

  while (s.startsWith("---\n")) {
    let end = s.indexOf("\n---\n", 4);
    let closeLen = "\n---\n".length;
    if (end === -1) {
      let close = s.indexOf("\n---", 4);
      while (close !== -1 && s[close + 4] === "-") {
        close = s.indexOf("\n---", close + 4);
      }
      end = close;
      closeLen = "\n---".length;
    }
    if (end === -1) break;

    const after = end + closeLen;
    s = s.slice(after);
    if (s.startsWith("\n")) s = s.slice(1);
    s = s.replace(/^\s+/, "");
  }

  return s.trim();
}

export async function listBlogPosts(): Promise<BlogPostMeta[]> {
  const { getAllBlogPosts } = await import("@/lib/blog");
  return getAllBlogPosts();
}

export async function readBlogPost(slug: string): Promise<BlogPost | null> {
  const { getBlogPost } = await import("@/lib/blog");
  return getBlogPost(slug);
}

export async function upsertBlogPost(input: AdminBlogPostUpsert): Promise<{ slug: string }> {
  const slug = normalizeSlug(input.slug);
  await fs.mkdir(POSTS_DIR, { recursive: true });
  const file = path.join(POSTS_DIR, `${slug}.md`);
  const fm = renderFrontmatter({ ...input, slug });
  const body = stripLeadingFrontmatterBlocks(input.content || "");
  await fs.writeFile(file, `${fm}${body}\n`, "utf8");
  return { slug };
}

export async function deleteBlogPost(slug: string): Promise<void> {
  const s = normalizeSlug(slug);
  const file = path.join(POSTS_DIR, `${s}.md`);
  await fs.rm(file, { force: true });
}

export async function saveBlogImage(fileName: string, bytes: Uint8Array): Promise<{ fileName: string }> {
  const safe = validateExistingImageFileName(safeFileName(fileName));
  await fs.mkdir(IMAGES_DIR, { recursive: true });
  await fs.mkdir(PUBLIC_IMAGES_DIR, { recursive: true });

  const dest = path.join(IMAGES_DIR, safe);
  await fs.writeFile(dest, bytes);

  const publicDest = path.join(PUBLIC_IMAGES_DIR, safe);
  await fs.writeFile(publicDest, bytes);

  return { fileName: safe };
}

export async function deleteBlogImage(fileName: string): Promise<void> {
  const safeName = validateExistingImageFileName(fileName);
  const contentPath = path.join(IMAGES_DIR, safeName);
  const publicPath = path.join(PUBLIC_IMAGES_DIR, safeName);
  await fs.rm(contentPath, { force: true });
  await fs.rm(publicPath, { force: true });
}
