<script lang="ts">
  import { onMount } from 'svelte';
  import DOMPurify from 'dompurify';
  import { appRpc } from './rpc.ts';

  interface Props {
    cwd: string;
  }

  let { cwd }: Props = $props();

  interface DocFile {
    name: string;
    path: string;
  }

  let files = $state<DocFile[]>([]);
  let selectedFile = $state<DocFile | null>(null);
  let content = $state('');
  let renderedHtml = $state('');
  let loading = $state(false);

  // Fix #1: Configure DOMPurify with safe tag whitelist
  const PURIFY_CONFIG = {
    ALLOWED_TAGS: ['h1', 'h2', 'h3', 'h4', 'p', 'a', 'strong', 'em', 'code', 'pre', 'ul', 'li', 'br'],
    ALLOWED_ATTR: ['href', 'target', 'class'],
  };

  function expandHome(p: string): string {
    if (p.startsWith('~/')) return p.replace('~', '/home/' + (typeof process !== 'undefined' ? process.env.USER : 'user'));
    return p;
  }

  async function loadFiles() {
    try {
      const resolved = expandHome(cwd);
      const res = await appRpc.request['files.list']({ path: resolved });
      if (res.error) return;
      files = res.entries
        .filter((e: { name: string; type: string }) => e.type === 'file' && e.name.endsWith('.md'))
        .map((e: { name: string }) => ({ name: e.name, path: `${resolved}/${e.name}` }));
    } catch (err) {
      console.error('[DocsTab] list error:', err);
    }
  }

  async function selectFile(file: DocFile) {
    selectedFile = file;
    loading = true;
    try {
      const res = await appRpc.request['files.read']({ path: file.path });
      if (res.error || !res.content) {
        content = '';
        renderedHtml = DOMPurify.sanitize(
          `<p class="doc-error">${escapeHtml(res.error ?? 'Empty file')}</p>`,
          PURIFY_CONFIG,
        );
      } else {
        content = res.content;
        renderedHtml = DOMPurify.sanitize(renderMarkdown(content), PURIFY_CONFIG);
      }
    } catch (err) {
      console.error('[DocsTab] read error:', err);
      renderedHtml = DOMPurify.sanitize('<p class="doc-error">Failed to read file</p>', PURIFY_CONFIG);
    }
    loading = false;
  }

  function escapeHtml(s: string): string {
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  }

  /** Simple markdown-to-HTML. All output is sanitized by DOMPurify before rendering. */
  function renderMarkdown(md: string): string {
    let html = md
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');

    // Code blocks
    html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_m, _lang, code) =>
      `<pre class="doc-code"><code>${code.trim()}</code></pre>`
    );
    // Inline code
    html = html.replace(/`([^`]+)`/g, '<code class="doc-inline-code">$1</code>');
    // Headers
    html = html.replace(/^#### (.+)$/gm, '<h4>$1</h4>');
    html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
    html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
    html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
    // Bold + italic
    html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
    html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
    html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
    // Links
    html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
    // Unordered lists
    html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
    html = html.replace(/(<li>[\s\S]*?<\/li>)/g, '<ul>$1</ul>');
    html = html.replace(/<\/ul>\s*<ul>/g, '');
    // Paragraphs (lines not already wrapped)
    html = html.replace(/^(?!<[huplo])((?!\s*$).+)$/gm, '<p>$1</p>');

    return html;
  }

  onMount(() => { loadFiles(); });
</script>

<div class="docs-tab">
  <div class="docs-sidebar">
    <div class="docs-header">Markdown files</div>
    {#if files.length === 0}
      <div class="docs-empty">No .md files in project</div>
    {:else}
      {#each files as file}
        <button
          class="docs-file"
          class:active={selectedFile?.path === file.path}
          onclick={() => selectFile(file)}
        >{file.name}</button>
      {/each}
    {/if}
  </div>

  <div class="docs-content">
    {#if loading}
      <div class="docs-placeholder">Loading...</div>
    {:else if !selectedFile}
      <div class="docs-placeholder">Select a file to view</div>
    {:else}
      <div class="docs-rendered">
        {@html renderedHtml}
      </div>
    {/if}
  </div>
</div>

<style>
  .docs-tab { display: flex; height: 100%; overflow: hidden; }

  .docs-sidebar {
    width: 10rem;
    flex-shrink: 0;
    background: var(--ctp-mantle);
    border-right: 1px solid var(--ctp-surface0);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }

  .docs-header {
    padding: 0.375rem 0.5rem;
    font-size: 0.625rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ctp-overlay0);
    border-bottom: 1px solid var(--ctp-surface0);
  }

  .docs-empty {
    padding: 1rem 0.5rem;
    font-size: 0.75rem;
    color: var(--ctp-overlay0);
    font-style: italic;
    text-align: center;
  }

  .docs-file {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.375rem 0.5rem;
    background: transparent;
    border: none;
    color: var(--ctp-subtext0);
    font-size: 0.75rem;
    cursor: pointer;
    font-family: var(--term-font-family);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: background 0.1s, color 0.1s;
  }

  .docs-file:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
  .docs-file.active { background: color-mix(in srgb, var(--ctp-blue) 12%, transparent); color: var(--ctp-blue); }

  .docs-content { flex: 1; overflow-y: auto; padding: 0.75rem; min-width: 0; }

  .docs-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--ctp-overlay0);
    font-size: 0.8125rem;
    font-style: italic;
  }

  .docs-rendered {
    font-family: var(--ui-font-family);
    font-size: 0.8125rem;
    color: var(--ctp-text);
    line-height: 1.6;
  }

  .docs-rendered :global(h1) { font-size: 1.25rem; color: var(--ctp-text); margin: 0.75rem 0 0.375rem; }
  .docs-rendered :global(h2) { font-size: 1.0625rem; color: var(--ctp-text); margin: 0.625rem 0 0.3rem; }
  .docs-rendered :global(h3) { font-size: 0.9375rem; color: var(--ctp-text); margin: 0.5rem 0 0.25rem; }
  .docs-rendered :global(p) { margin: 0.25rem 0; }
  .docs-rendered :global(a) { color: var(--ctp-blue); text-decoration: none; }
  .docs-rendered :global(a:hover) { text-decoration: underline; }
  .docs-rendered :global(strong) { color: var(--ctp-text); }
  .docs-rendered :global(ul) { padding-left: 1.25rem; margin: 0.25rem 0; }
  .docs-rendered :global(li) { margin: 0.125rem 0; }

  .docs-rendered :global(.doc-code) {
    background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
    border-radius: 0.25rem;
    padding: 0.5rem 0.625rem;
    overflow-x: auto;
    font-family: var(--term-font-family);
    font-size: 0.75rem;
    line-height: 1.5;
    margin: 0.375rem 0;
  }

  .docs-rendered :global(.doc-inline-code) {
    background: var(--ctp-surface0);
    padding: 0.1rem 0.25rem;
    border-radius: 0.2rem;
    font-family: var(--term-font-family);
    font-size: 0.75rem;
  }

  .docs-rendered :global(.doc-error) { color: var(--ctp-red); font-style: italic; }
</style>
