import * as path from "path";
import * as fs from "fs";
import * as mkdirp from "mkdirp";
import * as rimraf from "rimraf";
import { v4 as uuidv4 } from "uuid";
import { spawn } from "child_process";
import { ExportOptions, CourseMetadata } from "./types";
import { downloadS3Folder, getCourseMetadata } from "./shared";

// Convert markdown to HTML using pandoc
async function convertMarkdownToHtml(
  markdownPath: string,
  outputPath: string
): Promise<void> {
  return new Promise((resolve, reject) => {
    let stdout = "";
    let stderr = "";

    const pandocArgs = [
      markdownPath,
      "-o",
      outputPath,
      "--from",
      "markdown",
      "--to",
      "html",
      "--standalone",
      "--css",
      path.join(__dirname, "../templates/epub/epub.css"),
    ];

    console.log("Executing pandoc command:", "pandoc", pandocArgs.join(" "));
    console.log("Input file exists:", fs.existsSync(markdownPath));
    console.log(
      "Output directory exists:",
      fs.existsSync(path.dirname(outputPath))
    );

    const pandoc = spawn("pandoc", pandocArgs);

    pandoc.stdout.on("data", (data) => {
      stdout += data.toString();
    });

    pandoc.stderr.on("data", (data) => {
      stderr += data.toString();
    });

    pandoc.on("close", (code) => {
      if (code === 0) {
        resolve();
      } else {
        const errorMessage = `Pandoc process exited with code ${code}\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`;
        console.error("Pandoc Error Details:", errorMessage);
        reject(new Error(errorMessage));
      }
    });

    pandoc.on("error", (err) => {
      console.error("Pandoc spawn error:", err);
      reject(err);
    });
  });
}

// Generate EPUB using pandoc
async function generateEpub(
  htmlFiles: string[],
  outputPath: string,
  metadata: CourseMetadata
): Promise<void> {
  return new Promise((resolve, reject) => {
    let stdout = "";
    let stderr = "";

    // Include all HTML files in the EPUB
    const pandocArgs = [
      ...htmlFiles, // All HTML files
      "-o",
      outputPath,
      "--from",
      "html",
      "--to",
      "epub",
      "--metadata",
      `title=${metadata.title}`,
      "--metadata",
      `language=${metadata.language || "en"}`,
      "--metadata",
      `creator=FourGeeksAcademy`,
      "--metadata",
      `description=${metadata.description || ""}`,
      "--metadata",
      `subject=${metadata.technologies?.join(", ") || "Programming"}`,
      "--css",
      path.join(__dirname, "../templates/epub/epub.css"),
    ];

    console.log(
      "Executing pandoc EPUB command:",
      "pandoc",
      pandocArgs.join(" ")
    );
    console.log("HTML files to include:", htmlFiles);
    console.log(
      "All input files exist:",
      htmlFiles.every((file) => fs.existsSync(file))
    );
    console.log(
      "Output directory exists:",
      fs.existsSync(path.dirname(outputPath))
    );

    const pandoc = spawn("pandoc", pandocArgs);

    pandoc.stdout.on("data", (data) => {
      stdout += data.toString();
    });

    pandoc.stderr.on("data", (data) => {
      stderr += data.toString();
    });

    pandoc.on("close", (code) => {
      if (code === 0) {
        resolve();
      } else {
        const errorMessage = `Pandoc process exited with code ${code}\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`;
        console.error("Pandoc Error Details:", errorMessage);
        reject(new Error(errorMessage));
      }
    });

    pandoc.on("error", (err) => {
      console.error("Pandoc spawn error:", err);
      reject(err);
    });
  });
}

// Process exercises and convert to HTML
async function processExercises(
  exercisesDir: string,
  outputDir: string,
  language: string = "en"
): Promise<string[]> {
  const htmlFiles: string[] = [];

  console.log("Processing exercises directory:", exercisesDir);
  console.log("Output directory:", outputDir);
  console.log("Language:", language);

  if (!fs.existsSync(exercisesDir)) {
    console.log("Exercises directory does not exist:", exercisesDir);
    return htmlFiles;
  }

  console.log("Exercises directory exists, scanning for files...");

  const processDirectory = async (dir: string, relativePath: string = "") => {
    const entries = fs.readdirSync(dir, { withFileTypes: true });

    // Sort entries to prioritize README files first
    entries.sort((a, b) => {
      const aName = a.name.toLowerCase();
      const bName = b.name.toLowerCase();

      // Prioritize README files
      if (aName === "readme.md" && bName !== "readme.md") return -1;
      if (bName === "readme.md" && aName !== "readme.md") return 1;

      return aName.localeCompare(bName);
    });

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      const relativeFilePath = path.join(relativePath, entry.name);

      if (entry.isDirectory()) {
        console.log("Processing subdirectory:", relativeFilePath);
        // esling-disable-next-line
        await processDirectory(fullPath, relativeFilePath);
      } else if (
        entry.name.endsWith(".md") ||
        entry.name.endsWith(".markdown")
      ) {
        // Apply language filtering for all markdown files
        const baseName = entry.name.replace(/\.(md|markdown)$/, "");

        if (language === "en") {
          // For English: exclude files with language suffixes
          if (
            baseName.includes(".") &&
            /\.(es|fr|de|it|pt|ja|ko|zh|ar|ru)$/i.test(baseName)
          ) {
            console.log(
              "Skipping language-specific file for English:",
              relativeFilePath
            );
            continue;
          }
        } else {
          // For other languages: only include files with the correct language suffix
          if (!baseName.endsWith(`.${language}`)) {
            console.log(
              "Skipping file with different language:",
              relativeFilePath
            );
            continue;
          }
        }

        // Convert markdown to HTML
        console.log("Converting markdown file:", relativeFilePath);
        const htmlFileName = entry.name.replace(/\.(md|markdown)$/, ".html");
        const htmlPath = path.join(
          outputDir,
          relativeFilePath.replace(/\.(md|markdown)$/, ".html")
        );

        console.log("HTML output path:", htmlPath);
        mkdirp.sync(path.dirname(htmlPath));
        // esling-disable-next-line
        await convertMarkdownToHtml(fullPath, htmlPath);

        htmlFiles.push(htmlPath);
        console.log(
          "Successfully converted:",
          relativeFilePath,
          "->",
          htmlPath
        );
      } else if (entry.name.endsWith(".html")) {
        // Copy HTML files as-is
        console.log("Copying HTML file:", relativeFilePath);
        const destPath = path.join(outputDir, relativeFilePath);
        mkdirp.sync(path.dirname(destPath));
        // esling-disable-next-line
        fs.copyFileSync(fullPath, destPath);
        htmlFiles.push(destPath);
        console.log("Successfully copied:", relativeFilePath, "->", destPath);
      } else {
        console.log(
          "Skipping file:",
          relativeFilePath,
          "(not markdown or HTML)"
        );
      }
    }
  };

  await processDirectory(exercisesDir);
  return htmlFiles;
}

// Create EPUB table of contents
function createTocHtml(htmlFiles: string[], metadata: CourseMetadata): string {
  let tocContent = `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Table of Contents - ${metadata.title}</title>
    <link rel="stylesheet" href="epub.css">
</head>
<body>
    <h1>${metadata.title}</h1>
    <nav id="toc">
        <h2>Table of Contents</h2>
        <ol>
            <li><a href="main.html">Course Overview</a></li>
`;

  htmlFiles.forEach((file) => {
    const fileName = path.basename(file, ".html");
    const displayName = fileName
      .replace(/[-_]/g, " ")
      .replace(/\b\w/g, (l) => l.toUpperCase());
    // Use the full relative path for the href to ensure proper navigation
    const relativePath = file.replace(/.*\/html\//, "").replace(/\\/g, "/");
    tocContent += `            <li><a href="${relativePath}">${displayName}</a></li>\n`;
  });

  tocContent += `
        </ol>
    </nav>
</body>
</html>`;

  return tocContent;
}

export async function exportToEpub(options: ExportOptions): Promise<string> {
  const { courseSlug, bucket, outDir, language = "en" } = options;

  console.log("Starting EPUB export for course:", courseSlug);
  console.log("Output directory:", outDir);
  console.log("Language:", language);

  // 1. Create temporary folder
  const tmpName = uuidv4();
  const epubOutDir = path.join(outDir, tmpName);
  rimraf.sync(epubOutDir);
  mkdirp.sync(epubOutDir);

  console.log("Created temporary directory:", epubOutDir);

  // 2. Create EPUB template directory
  const epubTemplateDir = path.join(epubOutDir, "template");
  mkdirp.sync(epubTemplateDir);

  // 3. Download exercises and .learn from bucket
  console.log("Downloading exercises...");
  await downloadS3Folder(
    bucket,
    `courses/${courseSlug}/exercises/`,
    path.join(epubOutDir, "exercises")
  );
  console.log("Downloading .learn files...");
  await downloadS3Folder(
    bucket,
    `courses/${courseSlug}/.learn/`,
    path.join(epubOutDir, ".learn")
  );

  // 4. Read learn.json for course info
  console.log("Reading course metadata...");
  const learnJson = await getCourseMetadata(bucket, courseSlug);

  const metadata: CourseMetadata = {
    title: learnJson.title?.en || learnJson.title || courseSlug,
    description: learnJson.description?.en || learnJson.description,
    language: learnJson.language || "en",
    technologies: learnJson.technologies || [],
    difficulty: learnJson.difficulty || "beginner",
  };

  console.log("Course metadata:", metadata);

  // 5. Process exercises and convert to HTML
  console.log("Processing exercises and converting to HTML...");
  const htmlFiles = await processExercises(
    path.join(epubOutDir, "exercises"),
    path.join(epubOutDir, "html"),
    language
  );

  console.log("Generated HTML files:", htmlFiles.length);
  console.log("HTML files:", htmlFiles);

  // 6. Create table of contents
  const tocHtml = createTocHtml(htmlFiles, metadata);
  const tocPath = path.join(epubOutDir, "html", "toc.html");
  mkdirp.sync(path.dirname(tocPath));
  fs.writeFileSync(tocPath, tocHtml, "utf-8");

  // 7. Create main content file
  const mainContent = `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>${metadata.title}</title>
    <link rel="stylesheet" href="epub.css">
</head>
<body>
    <h1>${metadata.title}</h1>
    <p>${metadata.description || ""}</p>
    <p><strong>Technologies:</strong> ${
      metadata.technologies?.join(", ") || "Programming"
    }</p>
    <p><strong>Difficulty:</strong> ${metadata.difficulty}</p>
    <p><strong>Language:</strong> ${language}</p>
    <p><a href="toc.html">Go to Table of Contents</a></p>
</body>
</html>`;

  const mainContentPath = path.join(epubOutDir, "html", "main.html");
  fs.writeFileSync(mainContentPath, mainContent, "utf-8");

  // 8. Prepare all HTML files for EPUB generation
  const allHtmlFiles = [mainContentPath, tocPath, ...htmlFiles];
  console.log("All HTML files for EPUB:", allHtmlFiles);

  // 9. Generate EPUB using pandoc
  console.log("Generating EPUB file...");
  const epubPath = path.join(epubOutDir, `${courseSlug}.epub`);
  await generateEpub(allHtmlFiles, epubPath, metadata);

  console.log("EPUB generated successfully:", epubPath);

  // Clean up temporary files
  console.log("Cleaning up temporary files...");
  rimraf.sync(path.join(epubOutDir, "html"));
  rimraf.sync(path.join(epubOutDir, "exercises"));
  rimraf.sync(path.join(epubOutDir, ".learn"));

  console.log("EPUB export completed successfully");
  return epubPath;
}
