import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';
import { markdownToHtml } from '../src/lib/markdown';
import { HTMLElement, parse } from 'node-html-parser';
import { htmlToMarkdown } from '../src/lib/html';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export const allowedLinkTypes = [
  'video',
  'article',
  'opensource',
  'course',
  'website',
  'podcast',
] as const;

// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const allRoadmaps = await fs.readdir(ROADMAP_CONTENT_DIR);

const editorRoadmapIds = new Set<string>();
for (const roadmapId of allRoadmaps) {
  const roadmapFrontmatterDir = path.join(
    ROADMAP_CONTENT_DIR,
    roadmapId,
    `${roadmapId}.md`,
  );
  const roadmapFrontmatterRaw = await fs.readFile(
    roadmapFrontmatterDir,
    'utf-8',
  );
  const { data } = matter(roadmapFrontmatterRaw);

  const roadmapFrontmatter = data as RoadmapFrontmatter;
  if (roadmapFrontmatter.renderer === 'editor') {
    editorRoadmapIds.add(roadmapId);
  }
}

const publicRoadmapsContentDir = path.join('./public', 'roadmap-content');
const stats = await fs.stat(publicRoadmapsContentDir).catch(() => null);
if (!stats || !stats.isDirectory()) {
  await fs.mkdir(publicRoadmapsContentDir, { recursive: true });
}

for (const roadmapId of editorRoadmapIds) {
  console.log(`🚀 Starting ${roadmapId}`);

  const roadmapDir = path.join(
    ROADMAP_CONTENT_DIR,
    roadmapId,
    `${roadmapId}.json`,
  );
  const roadmapContent = await fs.readFile(roadmapDir, 'utf-8');
  let { nodes } = JSON.parse(roadmapContent) as {
    nodes: Node[];
  };
  nodes = nodes.filter(
    (node) =>
      node?.type &&
      ['topic', 'subtopic', 'todo'].includes(node.type) &&
      node.data?.label,
  );

  const roadmapContentDir = path.join(
    ROADMAP_CONTENT_DIR,
    roadmapId,
    'content',
  );
  const stats = await fs.stat(roadmapContentDir).catch(() => null);
  if (!stats || !stats.isDirectory()) {
    await fs.mkdir(roadmapContentDir, { recursive: true });
  }

  const roadmapContentFiles = await fs.readdir(roadmapContentDir, {
    recursive: true,
  });

  const contentMap: Record<
    string,
    {
      title: string;
      description: string;
      links: {
        title: string;
        url: string;
        type: string;
      }[];
    }
  > = {};

  for (const node of nodes) {
    const ndoeDirPatterWithoutExt = `${slugify(node.data.label)}@${node.id}`;
    const nodeDirPattern = `${ndoeDirPatterWithoutExt}.md`;
    if (!roadmapContentFiles.includes(nodeDirPattern)) {
      contentMap[nodeDirPattern] = {
        title: node.data.label,
        description: '',
        links: [],
      };

      continue;
    }

    const content = await fs.readFile(
      path.join(roadmapContentDir, nodeDirPattern),
      'utf-8',
    );
    const html = markdownToHtml(content, false);
    const rootHtml = parse(html);

    let ulWithLinks: HTMLElement | undefined;
    rootHtml.querySelectorAll('ul').forEach((ul) => {
      const listWithJustLinks = Array.from(ul.querySelectorAll('li')).filter(
        (li) => {
          const link = li.querySelector('a');
          return link && link.textContent?.trim() === li.textContent?.trim();
        },
      );

      if (listWithJustLinks.length > 0) {
        ulWithLinks = ul;
      }
    });

    const listLinks =
      ulWithLinks !== undefined
        ? Array.from(ulWithLinks.querySelectorAll('li > a'))
            .map((link) => {
              const typePattern = /@([a-z.]+)@/;
              let linkText = link.textContent || '';
              const linkHref = link.getAttribute('href') || '';
              let linkType = linkText.match(typePattern)?.[1] || 'article';
              linkType = allowedLinkTypes.includes(linkType as any)
                ? linkType
                : 'article';

              linkText = linkText.replace(typePattern, '');

              return {
                title: linkText,
                url: linkHref,
                type: linkType,
              };
            })
            .sort((a, b) => {
              const order = [
                'official',
                'opensource',
                'article',
                'video',
                'feed',
              ];
              return order.indexOf(a.type) - order.indexOf(b.type);
            })
        : [];

    const title = rootHtml.querySelector('h1');
    ulWithLinks?.remove();
    title?.remove();
    const htmlStringWithoutLinks = rootHtml.toString();
    const description = htmlToMarkdown(htmlStringWithoutLinks);

    contentMap[node.id] = {
      title: node.data.label,
      description,
      links: listLinks,
    };
  }

  await fs.writeFile(
    path.join(publicRoadmapsContentDir, `${roadmapId}.json`),
    JSON.stringify(contentMap, null, 2),
  );
  console.log(`✅ Finished ${roadmapId}`);
  console.log('-'.repeat(20));
}