import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

const postsDirectory = path.join(process.cwd(), 'content/posts');

export type PostMetadata = {
    title: string;
    date: string;
    description: string;
    slug: string;
    tags?: string[];
};

export type Post = {
    metadata: PostMetadata;
    content: string;
};

function readPostMetadata(data: Record<string, unknown>, slug: string): PostMetadata {
    if (
        typeof data.title !== 'string' ||
        typeof data.date !== 'string' ||
        typeof data.description !== 'string'
    ) {
        throw new Error(`Invalid frontmatter for post: ${slug}`);
    }

    if (!Number.isFinite(Date.parse(data.date))) {
        throw new Error(`Invalid date for post: ${slug}`);
    }

    return {
        title: data.title,
        date: data.date,
        description: data.description,
        slug,
        tags: Array.isArray(data.tags)
            ? data.tags.filter((tag): tag is string => typeof tag === 'string')
            : undefined,
    };
}

export function getPostSlugs() {
    if (!fs.existsSync(postsDirectory)) {
        return [];
    }
    return fs
        .readdirSync(postsDirectory, { withFileTypes: true })
        .filter((entry) => entry.isFile())
        .map((entry) => entry.name);
}

export function getPostBySlug(slug: string): Post {
    const realSlug = slug.replace(/\.mdx$/, '');

    if (/[\/\\]|\.\./.test(realSlug)) {
        throw new Error(`Invalid slug: ${realSlug}`);
    }

    const fullPath = path.join(postsDirectory, `${realSlug}.mdx`);

    let fileContents: string;
    try {
        fileContents = fs.readFileSync(fullPath, 'utf8');
    } catch {
        throw new Error(`Post not found: ${realSlug}`);
    }

    const { data, content } = matter(fileContents);

    return {
        metadata: readPostMetadata(data, realSlug),
        content,
    };
}

export function getAllPosts(): PostMetadata[] {
    const slugs = getPostSlugs();
    const posts = slugs
        .filter((slug) => slug.endsWith('.mdx'))
        .map((slug) => getPostBySlug(slug).metadata)
        // Sort posts by date in descending order
        .sort((post1, post2) => new Date(post2.date).getTime() - new Date(post1.date).getTime());
    return posts;
}
