import { readdir, readFile, writeFile, mkdir, stat } from 'node:fs/promises';
import path from 'node:path';
import matter from 'gray-matter';
import { classify, compareEntries } from './collections.js';

const SITE_DIR = process.env.SITE_DIR || '/site';
const CONTENT = path.join(SITE_DIR, 'content');

// Pfad-Sicherheit: relativer Pfad innerhalb content/, nur .md.
export function safeRel(rel) {
  if (!rel || typeof rel !== 'string') throw new Error('Pfad fehlt');
  const norm = path.normalize(rel).split(path.sep).join('/');
  if (norm.startsWith('..') || norm.startsWith('/') || norm.includes('../')) {
    throw new Error('Ungültiger Pfad');
  }
  if (!norm.endsWith('.md')) throw new Error('Nur .md erlaubt');
  return norm;
}

async function walk(dir) {
  const out = [];
  for (const e of await readdir(dir, { withFileTypes: true })) {
    const full = path.join(dir, e.name);
    if (e.isDirectory()) out.push(...(await walk(full)));
    else if (e.name.endsWith('.md')) out.push(full);
  }
  return out;
}

// authors-Frontmatter zu Array normalisieren (String oder Array erlaubt).
export function normAuthors(a) {
  if (Array.isArray(a)) return a.map(String).filter(Boolean);
  if (a) return [String(a)];
  return [];
}

// Hat diese E-Mail Zugriff (steht sie in der authors-Liste)?
export function hasAccess(authors, email) {
  const e = (email || '').toLowerCase();
  return normAuthors(authors).some((a) => a.toLowerCase() === e);
}

// Hugo-URL aus dem relativen Pfad.
export function urlFor(rel) {
  let p = rel.replace(/\.md$/, '');
  if (p === '_index') return '/';
  p = p.replace(/\/_index$/, '');
  return '/' + p + '/';
}

export async function listEntries() {
  // Lazy: only listing needs the site config, so importing files.js for its pure
  // helpers (safeRel/urlFor/…) never triggers the CMS_CONFIG load.
  const { config } = await import('./config.js');
  const files = await walk(CONTENT);
  const items = [];
  for (const full of files) {
    const rel = path.relative(CONTENT, full).split(path.sep).join('/');
    // Autor-Seiten werden über „Profil" verwaltet, nicht im Inhalts-Editor.
    if (rel === 'authors' || rel.startsWith('authors/')) continue;
    let data = {};
    try { data = matter(await readFile(full, 'utf8')).data || {}; } catch {}
    items.push({
      path: rel,
      title: data.title || rel,
      ...classify(rel, config.collections),
      color: data.color || null,
      layout: data.layout || null,
      draft: !!data.draft,
      date: data.date ? String(data.date).slice(0, 10) : null,
      authors: normAuthors(data.authors),
      url: urlFor(rel),
    });
  }
  // Reihenfolge aus der Config (collection.order), dann Datum, dann Titel.
  items.sort(compareEntries(config.collections));
  return items;
}

export async function readEntry(rel) {
  rel = safeRel(rel);
  const { data, content } = matter(await readFile(path.join(CONTENT, rel), 'utf8'));
  return { path: rel, url: urlFor(rel), frontmatter: data || {}, body: content || '' };
}

export async function entryExists(rel) {
  try { await stat(path.join(CONTENT, safeRel(rel))); return true; }
  catch { return false; }
}

export async function writeEntry(rel, frontmatter = {}, body = '') {
  rel = safeRel(rel);
  const full = path.join(CONTENT, rel);
  await mkdir(path.dirname(full), { recursive: true });

  // Leere Werte rauswerfen, damit das Frontmatter sauber bleibt.
  const fm = {};
  for (const [k, v] of Object.entries(frontmatter)) {
    if (v === '' || v === null || v === undefined) continue;
    if (Array.isArray(v) && v.length === 0) continue;
    fm[k] = v;
  }
  await writeFile(full, matter.stringify(body || '', fm), 'utf8');
  return rel;
}
