/**
 * @copyright NHCarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */

import { readFile } from "node:fs/promises";
import { readdir } from "node:fs/promises";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { navigation } from "../src/components/navigation.ts";
import matter from "gray-matter";

type Navigation = typeof navigation;
type NavigationItem = Array<{
    label: string
    link: string
    items?: Array<{
      label: string
      link: string
    }>
  }>

const excludedFiles = ["intro.mdx", "projects/_template.md"];

// this should recursively walk the specified directory and return a list of all files, prefixing them with nested paths.
// For example, calling walkDirectory() should return "/about/contact.md", "/about/mission.md", "/about/sustainability.md", etc.
const walkDirectory = async (directory = join(__dirname, "..", "src", "content", "docs"), prefix = ""): Promise<Array<string>> => {
    const filesAndDirectories = await readdir(directory, { withFileTypes: true });
    const pages = [];
    for (const fileOrDirectory of filesAndDirectories) {
        if (fileOrDirectory.isDirectory()) {
            pages.push(...(await walkDirectory(join(directory, fileOrDirectory.name), join(prefix, fileOrDirectory.name))));
        } else {
            pages.push(join(prefix, fileOrDirectory.name));
        }
    }
    return pages;
}

const flattenNavigation = (navigation: Navigation | NavigationItem): Array<{ label: string; link: string }> => {
    const items: Array<{ label: string; link: string }> = [];
    for (const item of navigation) {
        if ("items" in item && item.items) {
            items.push(...flattenNavigation(item.items as NavigationItem));
        } else {
            items.push({ label: item.label, link: item.link });
        }
    }
    return items;
}

describe("navigation", () => {
    it("should include all pages", async () => {
        expect.hasAssertions();
        let pages = await walkDirectory();
        pages = pages.filter((page) => !excludedFiles.includes(page));
        const flattenedNavigation = flattenNavigation(navigation);
        for (const page of pages) {
            const pageName = page.split(".")[0];
            const navItem = flattenedNavigation.find((item) => item.link === `/${pageName}`);
            expect(navItem, `Navigation item not found for page ${page}`).toBeDefined();
        }
    });

    it("should use page titles as navigation item labels", async () => {
        expect.hasAssertions();
        let pages = await walkDirectory();
        pages = pages.filter((page) => !excludedFiles.includes(page));
        const flattenedNavigation = flattenNavigation(navigation);
        for (const page of pages) {
            const pageName = page.split(".")[0];
            const navItem = flattenedNavigation.find((item) => item.link === `/${pageName}`);
            const pageContent = await readFile(join(__dirname, "..", "src", "content", "docs", page), "utf-8");
            const { data } = matter(pageContent);
            expect(navItem?.label, `Navigation item label for ${pageName} is not correct`).toBe(data.title);
        }
    });
});
