import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';

const IMAGE_REF_PATTERN = /!\[.*?\]\(([^)]+)\)/g;
const LINK_REF_PATTERN = /\[.*?\]\(([^)]+)\)/g;

function extractAssetPaths(markdownContent: string): string[] {
  const assets = new Set<string>();
  let match: RegExpExecArray | null;

  const patterns = [IMAGE_REF_PATTERN, LINK_REF_PATTERN];
  for (const pattern of patterns) {
    pattern.lastIndex = 0;
    while ((match = pattern.exec(markdownContent)) !== null) {
      const ref = match[1].split('#')[0].split('?')[0].trim();
      if (ref && !ref.startsWith('http://') && !ref.startsWith('https://') && !ref.startsWith('//')) {
        assets.add(ref);
      }
    }
  }

  return Array.from(assets);
}

export interface AutoDocsResult {
  tmpDir: string;
  docsDir: string;
  autoGeneratedMkdocsYml: boolean;
}

/**
 * Generate a temporary docs/ directory from *.md files in the given directory.
 * Each file gets full_path frontmatter so edits can be restored on shutdown.
 */
export function generateAutoDocs(
  sourceDir: string,
  tmpDir: string,
  additionalAssets?: string[]
): AutoDocsResult {
  const docsDir = path.join(tmpDir, 'docs');
  fs.mkdirSync(docsDir, { recursive: true });

  const mdFiles = fs.readdirSync(sourceDir).filter(
    (f) => f.endsWith('.md') && fs.statSync(path.join(sourceDir, f)).isFile()
  );

  if (mdFiles.length === 0) {
    throw new Error('No *.md files found in the current directory.');
  }

  const allAssets = new Set<string>(additionalAssets ?? []);

  for (const md of mdFiles) {
    const srcPath = path.join(sourceDir, md);
    const content = fs.readFileSync(srcPath, 'utf8');

    const frontmatter = `---${os.EOL}full_path: ${srcPath}${os.EOL}title: ${md}${os.EOL}---${os.EOL}`;
    fs.writeFileSync(path.join(docsDir, md), frontmatter + content);

    for (const asset of extractAssetPaths(content)) {
      const absAsset = path.resolve(sourceDir, asset);
      if (fs.existsSync(absAsset)) {
        allAssets.add(asset);
      }
    }
  }

  for (const asset of allAssets) {
    const srcAsset = path.resolve(sourceDir, asset);
    if (!fs.existsSync(srcAsset)) { continue; }

    const destAsset = path.join(docsDir, asset);
    const destDir = path.dirname(destAsset);
    fs.mkdirSync(destDir, { recursive: true });

    const stat = fs.statSync(srcAsset);
    if (stat.isDirectory()) {
      fs.cpSync(srcAsset, destAsset, { recursive: true });
    } else {
      fs.copyFileSync(srcAsset, destAsset);
    }
  }

  let autoGeneratedMkdocsYml = false;
  const mkdocsYmlPath = path.join(tmpDir, 'mkdocs.yml');
  if (!fs.existsSync(path.join(sourceDir, 'mkdocs.yml'))) {
    const siteName = path.basename(sourceDir);
    fs.writeFileSync(
      mkdocsYmlPath,
      `site_name: "${siteName}"${os.EOL}docs_dir: docs${os.EOL}`
    );
    autoGeneratedMkdocsYml = true;
  }

  return { tmpDir, docsDir, autoGeneratedMkdocsYml };
}

/**
 * Restore edited markdown files from the temp docs/ back to their
 * original locations, stripping the frontmatter we injected.
 */
export function restoreEditedDocs(docsDir: string, readonly: boolean): void {
  if (readonly) { return; }

  if (!fs.existsSync(docsDir)) { return; }

  const files = fs.readdirSync(docsDir).filter((f) => f.endsWith('.md'));
  for (const file of files) {
    const docPath = path.join(docsDir, file);
    const content = fs.readFileSync(docPath, 'utf8');

    const lines = content.split(/\r?\n/);
    if (lines[0] !== '---') {
      continue;
    }

    let fullPath: string | undefined;
    let endIndex = -1;
    for (let i = 1; i < lines.length; i++) {
      if (lines[i] === '---') {
        endIndex = i;
        break;
      }
      if (lines[i].startsWith('full_path: ')) {
        fullPath = lines[i].substring('full_path: '.length);
      }
    }

    if (fullPath && endIndex > 0) {
      const stripped = lines.slice(endIndex + 1).join(os.EOL);
      fs.writeFileSync(fullPath, stripped);
    }
  }
}

/**
 * Parse markdown content to find referenced assets.
 * Returns relative paths to images and linked files.
 */
export function detectAssets(markdownPath: string): string[] {
  const content = fs.readFileSync(markdownPath, 'utf8');
  const dir = path.dirname(markdownPath);
  const refs = extractAssetPaths(content);

  return refs.filter((ref) => {
    const absPath = path.resolve(dir, ref);
    return fs.existsSync(absPath);
  });
}
