import type { Route } from "@std/http/unstable-route";
import { withAuth } from "../../routes/middleware.ts";
import { renderHTML } from "../../utils/render.tsx";
import { codeToHtml } from "@shikijs/shiki";
import { DocsPage } from "./templates/DocsPage.tsx";
import { DocsIndexPage } from "./templates/DocsIndexPage.tsx";
import { render } from "preact-render-to-string";
import { CodeBlock } from "./templates/fragments/CodeBlock.tsx";
import { marked } from "marked";
import type { Tokens } from "marked";
import { markedHighlight } from "marked-highlight";

// Categorized documentation structure
const DOCS_CATEGORIES = [
  {
    category: "Getting Started",
    docs: [
      {
        slug: "intro",
        title: "Introduction",
        description: "Overview of Slices platform",
      },
      {
        slug: "getting-started",
        title: "Quick Start",
        description: "Learn how to set up and use Slices",
      },
    ],
  },
  {
    category: "Core Concepts",
    docs: [
      {
        slug: "concepts",
        title: "Core Concepts",
        description: "Understand slices, lexicons, and collections",
      },
    ],
  },
  {
    category: "Reference",
    docs: [
      {
        slug: "api-reference",
        title: "API Reference",
        description: "Complete endpoint documentation",
      },
      {
        slug: "graphql-api",
        title: "GraphQL API",
        description: "Query indexed data with GraphQL",
      },
      {
        slug: "sdk-usage",
        title: "SDK Usage",
        description: "Advanced client patterns and examples",
      },
    ],
  },
  {
    category: "Extensions",
    docs: [
      {
        slug: "plugins",
        title: "Plugins",
        description: "Extensions and tools for development",
      },
    ],
  },
];

// Flatten for backward compatibility
const AVAILABLE_DOCS = DOCS_CATEGORIES.flatMap((category) => category.docs);

const DOCS_PATH = Deno.env.get("DOCS_PATH") || "../docs";

async function readMarkdownFile(slug: string): Promise<string | null> {
  try {
    const filePath = `${DOCS_PATH}/${slug}.md`;
    const content = await Deno.readTextFile(filePath);
    return content;
  } catch (error) {
    console.error(`Failed to read ${slug}.md:`, error);
    return null;
  }
}

// Decode HTML entities
function decodeHtmlEntities(text: string): string {
  const entities: Record<string, string> = {
    "&amp;": "&",
    "&lt;": "<",
    "&gt;": ">",
    "&quot;": '"',
    "&#39;": "'",
    "&nbsp;": " ",
  };

  return text.replace(/&[^;]+;/g, (entity) => entities[entity] || entity);
}

// Extract headers for table of contents
function extractHeaders(html: string) {
  const headerRegex = /<h([1-6])[^>]*>(.*?)<\/h[1-6]>/g;
  const headers: Array<{ level: number; text: string; id: string }> = [];

  let match;
  while ((match = headerRegex.exec(html)) !== null) {
    const level = parseInt(match[1]);
    const rawText = match[2].replace(/<[^>]+>/g, ""); // Strip HTML tags
    const text = decodeHtmlEntities(rawText); // Decode HTML entities
    const id = text
      .toLowerCase()
      .replace(/[^\w\s-]/g, "") // Remove special characters
      .replace(/\s+/g, "-") // Replace spaces with hyphens
      .trim();

    headers.push({ level, text, id });
  }

  return headers;
}

// Markdown to HTML converter using marked with custom renderer and Shiki
async function markdownToHtml(markdown: string): Promise<string> {
  // Configure marked with highlight extension for code blocks
  marked.use(
    markedHighlight({
      async: true,
      async highlight(code, lang, info) {
        // Parse filename from info string (e.g., "javascript filename.js")
        const parts = (info || "").split(/\s+/);
        const filename =
          parts.length > 1 ? parts.slice(1).join(" ") : undefined;

        // Handle Mermaid diagrams - client-side rendering
        if (lang === "mermaid") {
          return `
          <div class="my-8 w-full overflow-x-auto">
            <div class="mermaid w-full" style="font-size: 16px;">${code}</div>
          </div>
        `;
        }

        const highlightedCode = await codeToHtml(code, {
          lang: lang || "text",
          themes: {
            light: "github-light",
            dark: "github-dark",
          },
        });

        // Generate the complete code block using composition
        if (filename) {
          return render(
            <CodeBlock>
              <CodeBlock.Header>
                <span>{filename}</span>
                <CodeBlock.CopyButton />
              </CodeBlock.Header>
              <CodeBlock.Code highlightedCode={highlightedCode} />
            </CodeBlock>
          );
        } else {
          return render(<CodeBlock.Code highlightedCode={highlightedCode} />);
        }
      },
      langPrefix: "language-",
    })
  );

  // Configure marked with custom renderer
  const renderer = new marked.Renderer();

  // Custom inline code renderer
  renderer.codespan = function (token: Tokens.Codespan) {
    return `<code class="bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 px-1.5 py-0.5 rounded text-sm font-mono font-normal">${token.text}</code>`;
  };

  // Custom header renderer with IDs and styling
  renderer.heading = function (token: Tokens.Heading) {
    const text = this.parser.parseInline(token.tokens);
    const level = token.depth;
    const id = text
      .toLowerCase()
      .replace(/<[^>]+>/g, "") // Strip HTML tags
      .replace(/[^\w\s-]/g, "") // Remove special characters
      .replace(/\s+/g, "-") // Replace spaces with hyphens
      .trim();

    const styles = {
      1: "text-2xl font-bold text-zinc-900 dark:text-white mt-10 mb-6",
      2: "text-xl font-bold text-zinc-900 dark:text-white mt-10 mb-4",
      3: "text-lg font-semibold text-zinc-900 dark:text-white mt-8 mb-4",
      4: "text-base font-semibold text-zinc-900 dark:text-white mt-6 mb-3",
      5: "text-sm font-semibold text-zinc-900 dark:text-white mt-4 mb-2",
      6: "text-xs font-semibold text-zinc-900 dark:text-white mt-4 mb-2",
    };

    return `<h${level} id="${id}" class="${
      styles[level as keyof typeof styles]
    }">${text}</h${level}>`;
  };

  // Custom link renderer to handle .md links
  renderer.link = function (token: Tokens.Link) {
    let href = token.href;
    const title = token.title;
    const text = this.parser.parseInline(token.tokens);

    // Convert relative .md links to docs routes
    if (href.endsWith(".md") && !href.startsWith("http")) {
      const slug = href.replace(/^\.\//, "").replace(/\.md$/, "");
      href = `/docs/${slug}`;
    }

    const titleAttr = title ? ` title="${title}"` : "";
    return `<a href="${href}"${titleAttr} class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200 underline">${text}</a>`;
  };

  // Custom paragraph renderer
  renderer.paragraph = function (token: Tokens.Paragraph) {
    const text = this.parser.parseInline(token.tokens);
    return `<p class="mb-4 leading-relaxed text-zinc-700 dark:text-zinc-300">${text}</p>`;
  };

  // Custom list renderer - wrap with classes
  const originalList = renderer.list.bind(renderer);
  renderer.list = function(token: Tokens.List) {
    const html = originalList(token);
    const type = token.ordered ? 'ol' : 'ul';
    const classNames = token.ordered
      ? 'list-decimal list-inside my-4 text-zinc-700 dark:text-zinc-300'
      : 'list-disc list-inside my-4 text-zinc-700 dark:text-zinc-300';

    return html.replace(new RegExp(`<${type}>`), `<${type} class="${classNames}">`);
  };

  // Custom list item renderer
  const originalListitem = renderer.listitem.bind(renderer);
  renderer.listitem = function(token: Tokens.ListItem) {
    const html = originalListitem(token);
    return html.replace('<li>', '<li class="mb-1">');
  };

  // Custom strong/bold renderer
  renderer.strong = function (token: Tokens.Strong) {
    const text = this.parser.parseInline(token.tokens);
    return `<strong class="font-semibold text-zinc-900 dark:text-white">${text}</strong>`;
  };

  // Custom emphasis/italic renderer
  renderer.em = function (token: Tokens.Em) {
    const text = this.parser.parseInline(token.tokens);
    return `<em class="italic">${text}</em>`;
  };

  // Custom code block renderer - return content as-is since marked-highlight handles it
  renderer.code = function (token: Tokens.Code) {
    return token.text; // marked-highlight has already processed this
  };

  // Custom table renderer
  renderer.table = function (token: Tokens.Table) {
    const header = token.header
      .map((cell: Tokens.TableCell, i: number) => {
        const text = this.parser.parseInline(cell.tokens);
        const align = token.align[i]
          ? ` style="text-align: ${token.align[i]}"`
          : "";
        return `<th class="px-4 py-2 text-left text-sm font-semibold text-zinc-900 dark:text-white border-b-2 border-zinc-200 dark:border-zinc-700"${align}>${text}</th>`;
      })
      .join("");

    const rows = token.rows
      .map((row: Tokens.TableCell[]) => {
        const cells = row
          .map((cell: Tokens.TableCell, i: number) => {
            const text = this.parser.parseInline(cell.tokens);
            const align = token.align[i]
              ? ` style="text-align: ${token.align[i]}"`
              : "";
            return `<td class="px-4 py-2 text-sm text-zinc-700 dark:text-zinc-300 border-b border-zinc-100 dark:border-zinc-800"${align}>${text}</td>`;
          })
          .join("");
        return `<tr class="hover:bg-zinc-50 dark:hover:bg-zinc-800/50">${cells}</tr>`;
      })
      .join("");

    return `<div class="overflow-x-auto my-6">
      <table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-700">
        <thead class="bg-zinc-50 dark:bg-zinc-800/50">
          <tr>${header}</tr>
        </thead>
        <tbody class="bg-white dark:bg-zinc-900 divide-y divide-zinc-100 dark:divide-zinc-800">
          ${rows}
        </tbody>
      </table>
    </div>`;
  };

  // Set options and use the custom renderer
  marked.setOptions({
    renderer: renderer,
    gfm: true,
    breaks: false, // This helps with multi-line list items
    pedantic: false,
  });

  // Parse markdown to HTML using marked (this handles multi-line list items properly)
  const html = await marked.parse(markdown);

  return html;
}

async function handleDocsIndex(request: Request): Promise<Response> {
  const { currentUser } = await withAuth(request);
  return renderHTML(
    <DocsIndexPage categories={DOCS_CATEGORIES} currentUser={currentUser} />
  );
}

async function handleDocsPage(request: Request): Promise<Response> {
  const { currentUser } = await withAuth(request);
  const url = new URL(request.url);
  const slug = url.pathname.split("/")[2]; // /docs/{slug}

  if (!slug) {
    // Redirect to docs index
    return new Response(null, {
      status: 302,
      headers: { Location: "/docs" },
    });
  }

  // Check if slug is valid
  const docInfo = AVAILABLE_DOCS.find((doc) => doc.slug === slug);
  if (!docInfo) {
    return new Response("Documentation page not found", { status: 404 });
  }

  // Read markdown content
  const markdownContent = await readMarkdownFile(slug);
  if (!markdownContent) {
    return new Response("Documentation content not found", { status: 404 });
  }

  // Convert to HTML with Shiki syntax highlighting
  const htmlContent = await markdownToHtml(markdownContent);

  // Extract headers for table of contents
  const headers = extractHeaders(htmlContent);

  return renderHTML(
    <DocsPage
      title={docInfo.title}
      content={htmlContent}
      headers={headers}
      docs={AVAILABLE_DOCS}
      categories={DOCS_CATEGORIES}
      currentSlug={slug}
      currentUser={currentUser}
    />
  );
}

// ============================================================================
// ROUTE EXPORTS
// ============================================================================

export const docsRoutes: Route[] = [
  // Docs index page
  {
    method: "GET",
    pattern: new URLPattern({ pathname: "/docs" }),
    handler: handleDocsIndex,
  },
  // Individual docs pages
  {
    method: "GET",
    pattern: new URLPattern({ pathname: "/docs/:slug" }),
    handler: handleDocsPage,
  },
];
