#!/usr/bin/env node

/**
 * =====================================================
 * Chrome 扩展导出转换器
 * 
 * 专门处理通过 Chrome 扩展本项目导出的 HTML 文件
 * 
 * 功能：
 * 1. 转换 index.html 为 React 组件
 * 2. 智能处理字体：CDN 保留链接，本地文件复制
 * 3. 复制静态资源（图片、字体）
 * 4. 保留完整的 style.css 样式
 * =====================================================
 */

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { assertValidGeneratedTsx } from './utils/generatedTsxValidator.mjs';

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

const CONFIG = {
  projectRoot: path.resolve(__dirname, '..'),
  pagesDir: path.resolve(__dirname, '../src/prototypes')
};

const JSX_ATTRIBUTE_REPLACEMENTS = [
  ['class', 'className'],
  ['for', 'htmlFor'],
  ['tabindex', 'tabIndex'],
  ['readonly', 'readOnly'],
  ['maxlength', 'maxLength'],
  ['minlength', 'minLength'],
  ['colspan', 'colSpan'],
  ['rowspan', 'rowSpan'],
  ['viewbox', 'viewBox'],
  ['preserveaspectratio', 'preserveAspectRatio'],
  ['clip-path', 'clipPath'],
  ['fill-rule', 'fillRule'],
  ['clip-rule', 'clipRule'],
  ['stroke-width', 'strokeWidth'],
  ['stroke-dasharray', 'strokeDasharray'],
  ['stroke-dashoffset', 'strokeDashoffset'],
  ['stroke-linecap', 'strokeLinecap'],
  ['stroke-linejoin', 'strokeLinejoin'],
  ['stroke-miterlimit', 'strokeMiterlimit'],
  ['stroke-opacity', 'strokeOpacity'],
  ['fill-opacity', 'fillOpacity'],
  ['stop-color', 'stopColor'],
  ['stop-opacity', 'stopOpacity'],
  ['xlink:href', 'xlinkHref'],
  ['xmlns:xlink', 'xmlnsXlink'],
];

function log(message, type = 'info') {
  const prefix = { info: '✓', warn: '⚠', error: '✗', progress: '⏳' }[type] || 'ℹ';
  console.log(`${prefix} ${message}`);
}

function ensureDir(dirPath) {
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
  }
}

function normalizeRelativeDir(value) {
  return String(value ?? '')
    .replace(/\\/g, '/')
    .split('/')
    .filter(Boolean)
    .join('/');
}

function isSafeRelativeDir(value) {
  if (!value) return false;
  if (value.startsWith('/') || value.startsWith('~')) return false;
  const segments = value.split('/');
  if (segments.length === 0) return false;
  return segments.every((segment) => {
    if (!segment || segment === '.' || segment === '..') return false;
    return !/[\\/]/.test(segment);
  });
}

/**
 * 判断是否为 CDN 链接
 */
function isCDNUrl(url) {
  return url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//');
}

/**
 * 递归复制目录
 */
function copyDirectory(src, dest) {
  if (!fs.existsSync(src)) return 0;
  
  ensureDir(dest);
  const entries = fs.readdirSync(src, { withFileTypes: true });
  let count = 0;
  
  for (const entry of entries) {
    const srcPath = path.join(src, entry.name);
    const destPath = path.join(dest, entry.name);
    
    if (entry.isDirectory()) {
      count += copyDirectory(srcPath, destPath);
    } else {
      fs.copyFileSync(srcPath, destPath);
      count++;
    }
  }
  
  return count;
}

/**
 * 提取 head 内容（仅处理外部资源和字体）
 */
function extractHeadContent(html) {
  const headMatch = html.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
  if (!headMatch) return { scripts: [], links: [] };
  
  const headContent = headMatch[1];
  const scripts = [];
  const links = [];
  
  // 提取 script 标签（排除 Tailwind CDN）
  const scriptRegex = /<script([^>]*)>([\s\S]*?)<\/script>/gi;
  let match;
  while ((match = scriptRegex.exec(headContent)) !== null) {
    const attrs = match[1];
    const content = match[2].trim();
    
    const srcMatch = attrs.match(/src=["']([^"']+)["']/);
    if (srcMatch) {
      const src = srcMatch[1].replace(/&amp;/g, '&');
      // 跳过 Tailwind CDN
      if (src.includes('tailwindcss.com')) continue;
      
      scripts.push({ 
        src,
        id: attrs.match(/id=["']([^"']+)["']/)?.[1]
      });
    } else if (content) {
      const id = attrs.match(/id=["']([^"']+)["']/)?.[1];
      scripts.push({ id, content });
    }
  }
  
  // 提取 link 标签
  const linkRegex = /<link[^>]*>/gi;
  while ((match = linkRegex.exec(headContent)) !== null) {
    const tag = match[0];
    const href = tag.match(/href=["']([^"']+)["']/)?.[1];
    if (href) {
      links.push({
        href: href.replace(/&amp;/g, '&'),
        rel: tag.match(/rel=["']([^"']+)["']/)?.[1] || 'stylesheet',
        crossorigin: tag.includes('crossorigin')
      });
    }
  }
  
  return { scripts, links };
}

/**
 * 转义文本节点中的花括号
 * 只处理标签之间的文本内容，不处理属性值
 */
function escapeTextBraces(html) {
  const parts = [];
  let lastIndex = 0;
  const tagRegex = /<[^>]+>/g;
  let match;
  
  while ((match = tagRegex.exec(html)) !== null) {
    // 提取标签之前的文本
    const textBefore = html.substring(lastIndex, match.index);
    if (textBefore) {
      // 转义文本中的花括号 - 使用占位符避免重复替换
      const escaped = textBefore
        .replace(/\{/g, "__LBRACE__")
        .replace(/\}/g, "__RBRACE__");
      parts.push(escaped);
    }
    // 添加标签本身（不转义）
    parts.push(match[0]);
    lastIndex = tagRegex.lastIndex;
  }
  
  // 添加最后一段文本
  const textAfter = html.substring(lastIndex);
  if (textAfter) {
    const escaped = textAfter
      .replace(/\{/g, "__LBRACE__")
      .replace(/\}/g, "__RBRACE__");
    parts.push(escaped);
  }
  
  return parts.join('')
    .replace(/__LBRACE__/g, "{'{'}")
	    .replace(/__RBRACE__/g, "{'}'}")
}

function convertCommonAttributesToJSX(content) {
  let nextContent = content;

  JSX_ATTRIBUTE_REPLACEMENTS.forEach(([from, to]) => {
    nextContent = nextContent.replace(new RegExp(`(\\s)${from}=`, 'gi'), `$1${to}=`);
  });

  return nextContent;
}

function createCommentPlaceholders(content) {
  const comments = [];
  const withPlaceholders = content.replace(/<!--([\s\S]*?)-->/g, (_, commentBody) => {
    const placeholder = `__HTML_COMMENT_${comments.length}__`;
    comments.push(`{/* ${commentBody} */}`);
    return placeholder;
  });

  return { withPlaceholders, comments };
}

function restoreCommentPlaceholders(content, comments) {
  return comments.reduce(
    (currentContent, comment, index) => currentContent.replaceAll(`__HTML_COMMENT_${index}__`, comment),
    content,
  );
}

function convertHtmlToJSX(content) {
  let nextContent = convertCommonAttributesToJSX(content)
    .replace(/(<pre[^>]*>)([\s\S]*?)(<\/pre>)/gi, (_, openTag, preContent) => {
      const escapedContent = preContent
        .replace(/\\/g, '\\\\')
        .replace(/`/g, '\\`')
        .replace(/\$/g, '\\$')
        .replace(/\{/g, '\\{');
      return `${openTag.slice(0, -1)} dangerouslySetInnerHTML={{ __html: \`${escapedContent}\` }} />`;
    })
    .replace(/style='([^']*)'/gi, (_, styleStr) => convertStyleToJSX(styleStr))
    .replace(/style="([^"]*)"/gi, (_, styleStr) => convertStyleToJSX(styleStr))
    .replace(/<\/(br|hr|img|input|meta|link)>/gi, '')
    .replace(/<(br|hr|img|input|meta|link)([^>]*)>/gi, '<$1$2 />')
    .replace(/<body\b([^>]*)>/gi, '<div data-chrome-export-body="true"$1>')
    .replace(/<\/body>/gi, '</div>')
    .replace(/&lt;\//g, '__LTSLASH__')
    .replace(/&lt;/g, '__LT__')
    .replace(/&gt;/g, '__GT__')
    .replace(/&amp;/g, '__AMP__');

  const { withPlaceholders, comments } = createCommentPlaceholders(nextContent);
  nextContent = escapeTextBraces(withPlaceholders);
  nextContent = restoreCommentPlaceholders(nextContent, comments);

  return nextContent
    .replace(/__LTSLASH__/g, "{'</'}")
    .replace(/__LT__/g, "{'<'}")
    .replace(/__GT__/g, "{'>'}")
    .replace(/__AMP__/g, '&');
}

/**
 * 提取并转换 body 内容
 */
function extractBodyContent(html) {
  const bodyMatch = html.match(/(<body[^>]*>)([\s\S]*?)(<\/body>)/i);
  if (!bodyMatch) return '';
  
  const [, openTag, innerContent] = bodyMatch;
  
  // 移除 <root> 标签（Chrome 扩展导出特有的包装标签）
  let cleanedContent = innerContent.trim()
    .replace(/^\s*<root>\s*/i, '')
    .replace(/\s*<\/root>\s*$/i, '');

  const convertedOpenTag = convertCommonAttributesToJSX(openTag)
    .replace(/^<body\b/i, '<div data-chrome-export-root="true"');
  const content = convertHtmlToJSX(cleanedContent);

  return `${convertedOpenTag}\n${content}\n    </div>`;
}

function convertStyleToJSX(styleStr) {
  if (!styleStr.trim()) return 'style={{}}';
  
  // 先解码 HTML 实体
  const decodedStr = styleStr
    .replace(/&quot;/g, '"')
    .replace(/&apos;/g, "'")
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&amp;/g, '&');
  
  const styles = [];
  let currentProp = '';
  let inUrl = false;
  
  for (let i = 0; i < decodedStr.length; i++) {
    const char = decodedStr[i];
    if (char === '(' && decodedStr.substring(i - 3, i) === 'url') inUrl = true;
    else if (char === ')' && inUrl) inUrl = false;
    
    if (char === ';' && !inUrl) {
      if (currentProp.trim()) styles.push(currentProp.trim());
      currentProp = '';
    } else {
      currentProp += char;
    }
  }
  if (currentProp.trim()) styles.push(currentProp.trim());
  
  const jsxStyles = styles
    .filter(s => s.includes(':'))
    .map(s => {
      const colonIndex = s.indexOf(':');
      const key = s.substring(0, colonIndex).trim();
      const value = s.substring(colonIndex + 1).trim();
      if (!key || !value) return '';
      
      const camelKey = key.startsWith('-')
        ? JSON.stringify(key)
        : key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
      let jsxValue;
      if (value.startsWith('url(') || value.includes('var(')) {
        jsxValue = `'${value.replace(/'/g, "\\'")}'`;
      } else if (/^-?\d+(\.\d+)?$/.test(value)) {
        jsxValue = value;
      } else {
        // 转义单引号和反斜杠
        const escapedValue = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
        jsxValue = `'${escapedValue}'`;
      }
      return `${camelKey}: ${jsxValue}`;
    })
    .filter(Boolean)
    .join(', ');
  
  return `style={{ ${jsxStyles} }}`;
}

/**
 * 生成组件代码
 */
function normalizeDisplayName(displayName) {
  const text = String(displayName ?? '').trim();
  const singleLine = text.replace(/\r?\n/g, ' ');
  const safeText = singleLine.replace(/\*\//g, '* /');
  return safeText.slice(0, 200);
}

function generateComponent(pageSlug, displayName, bodyContent, headContent) {
  const componentName = pageSlug
    .split(/[-_\s]+/)
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join('');
  const safeDisplayName = normalizeDisplayName(displayName || pageSlug);
  
  let cleanedContent = bodyContent.trim();
  if (cleanedContent.startsWith('{/*')) {
    const firstTagIndex = cleanedContent.indexOf('<');
    if (firstTagIndex > 0) {
      cleanedContent = cleanedContent.substring(firstTagIndex);
    }
  }
  
  const needsWrapper = !isWrappedInSingleElement(cleanedContent);
  const finalContent = needsWrapper ? `<>\n${cleanedContent}\n    </>` : cleanedContent;
  
  // 生成注入代码
  let injectionCode = '';
  
  if (headContent.links.length > 0 || headContent.scripts.length > 0) {
    const hasExternalScripts = headContent.scripts.some(s => s.src);
    
    injectionCode = `
  // 动态注入外部资源
  React.useEffect(function () {
    const injected: (HTMLElement)[] = [];
    `;
    
    if (headContent.links.length > 0) {
      injectionCode += `
    // 注入 links
    ${JSON.stringify(headContent.links)}.forEach(function (linkInfo: any) {
      const existing = document.querySelector(\`link[href="\${linkInfo.href}"]\`);
      if (!existing) {
        const link = document.createElement('link');
        link.rel = linkInfo.rel;
        link.href = linkInfo.href;
        if (linkInfo.crossorigin) link.crossOrigin = 'anonymous';
        document.head.appendChild(link);
        injected.push(link);
      }
    });
    `;
    }
    
    if (hasExternalScripts) {
      injectionCode += `
    // 注入外部脚本
    ${JSON.stringify(headContent.scripts.filter(s => s.src))}.forEach(function (scriptInfo: any) {
      const existing = document.querySelector(\`script[src="\${scriptInfo.src}"]\`);
      if (!existing) {
        const script = document.createElement('script');
        if (scriptInfo.id) script.id = scriptInfo.id;
        script.src = scriptInfo.src;
        document.head.appendChild(script);
        injected.push(script);
      }
    });
    `;
    }
    
    injectionCode += `
    return function () {
      injected.forEach(function (el) {
        if (el.parentNode) el.parentNode.removeChild(el);
      });
    };
  }, []);
`;
  }
  
  return `/**
 * @name ${safeDisplayName}
 * 
 * 参考资料：
 * - /rules/development-guide.md
 * - /skills/default-resource-recommendations/SKILL.md
 */

import './style.css';
import React, { forwardRef, useImperativeHandle } from 'react';
import type { AxureProps, AxureHandle } from '../../common/axure-types';

class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean; error: Error | null }
> {
  constructor(props: any) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error('[${pageSlug}] 组件渲染错误:', error);
    console.error('[${pageSlug}] 错误详情:', errorInfo);
    console.error('[${pageSlug}] 错误堆栈:', error.stack);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div style={{ padding: '20px', color: 'red', border: '2px solid red', margin: '20px' }}>
          <h2>组件渲染失败: ${safeDisplayName}</h2>
          <pre style={{ whiteSpace: 'pre-wrap', fontSize: '12px' }}>
            {this.state.error?.toString()}
            {this.state.error?.stack}
          </pre>
        </div>
      );
    }

    return this.props.children;
  }
}

const Component = forwardRef(function ${componentName}(
  innerProps: AxureProps,
  ref: React.ForwardedRef<AxureHandle>,
) {
  console.log('[${pageSlug}] 组件开始渲染');
  
  useImperativeHandle(ref, function () {
    return {
      getVar: function () { return undefined; },
      fireAction: function () {},
      eventList: [],
      actionList: [],
      varList: [],
      configList: [],
      dataList: []
    };
  }, []);
${injectionCode}
  console.log('[${pageSlug}] 准备返回 JSX');
  
  try {
    return (
${finalContent.split('\n').map(line => '      ' + line).join('\n')}
    );
  } catch (error) {
    console.error('[${pageSlug}] JSX 渲染错误:', error);
    throw error;
  }
});

const WrappedComponent = forwardRef(function WrappedComponent(
  props: AxureProps,
  ref: React.ForwardedRef<AxureHandle>,
) {
  return (
    <ErrorBoundary>
      <Component {...props} ref={ref} />
    </ErrorBoundary>
  );
});

export default WrappedComponent;
`;
}

function isWrappedInSingleElement(content) {
  const trimmed = content.trim();
  if (!trimmed.startsWith('<')) return false;
  if (trimmed.startsWith('<body')) return trimmed.endsWith('</body>');
  
  const firstTagMatch = trimmed.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
  if (!firstTagMatch) return false;
  
  const tagName = firstTagMatch[1];
  const closingTag = `</${tagName}>`;
  if (!trimmed.endsWith(closingTag)) return false;
  
  const openCount = (trimmed.match(new RegExp(`<${tagName}[\\s>]`, 'g')) || []).length;
  const closeCount = (trimmed.match(new RegExp(`</${tagName}>`, 'g')) || []).length;
  return openCount === closeCount && openCount === 1;
}

/**
 * 生成 CSS 文件（仅处理外部 style.css）
 */
function generateStyleCSS(fonts, sourcePath) {
  let css = '@import "tailwindcss";\n';
  
  // 处理字体
  if (fonts && fonts.length > 0) {
    css += '\n/* 字体定义 */\n';
    
    const cdnFonts = fonts.filter(f => f.isCDN);
    const localFonts = fonts.filter(f => !f.isCDN);
    
    if (cdnFonts.length > 0) {
      css += '\n/* CDN 字体（保留原始链接） */\n';
      cdnFonts.forEach(font => {
        css += font.rule + '\n\n';
      });
    }
    
    if (localFonts.length > 0) {
      css += '\n/* 本地字体（已复制到 assets 目录） */\n';
      localFonts.forEach(font => {
        // 将字体路径改为相对于 style.css 的路径
        const modifiedRule = font.rule.replace(
          /url\(['"]?([^'")\s]+)['"]?\)/g,
          (match, url) => `url('./${url}')`
        );
        css += modifiedRule + '\n\n';
      });
    }
  }
  
  // 读取外部 CSS 文件的完整内容（排除字体定义）
  const externalCSSPath = path.join(sourcePath, 'style.css');
  if (fs.existsSync(externalCSSPath)) {
    const externalCSS = fs.readFileSync(externalCSSPath, 'utf8');
    // 移除 @font-face 规则（已单独处理）
    const withoutFontFace = externalCSS.replace(/@font-face\s*\{[^}]+\}/g, '').trim();
    if (withoutFontFace) {
      css += '\n/* 样式类定义（来自 style.css）*/\n';
      css += withoutFontFace + '\n';
    }
  }
  
  return css;
}

/**
 * 从外部 CSS 文件提取字体
 */
function extractFontsFromCSS(cssPath) {
  if (!fs.existsSync(cssPath)) return [];
  
  const css = fs.readFileSync(cssPath, 'utf8');
  const fonts = [];
  
  const fontFaceRegex = /@font-face\s*\{([^}]+)\}/g;
  let match;
  while ((match = fontFaceRegex.exec(css)) !== null) {
    const fontRule = match[1];
    const srcMatch = fontRule.match(/src:\s*url\(['"]?([^'")\s]+)['"]?\)/);
    const familyMatch = fontRule.match(/font-family:\s*['"]([^'"]+)['"]/);
    
    if (srcMatch && familyMatch) {
      const fontSrc = srcMatch[1];
      const fontFamily = familyMatch[1];
      
      fonts.push({
        family: fontFamily,
        src: fontSrc,
        isCDN: isCDNUrl(fontSrc),
        rule: match[0]
      });
    }
  }
  
  return fonts;
}

/**
 * 转换单个页面
 */
function convertPage(sourcePath, outputDir, pageSlug, displayName) {
  log(`正在转换页面: ${pageSlug}`, 'progress');
  
  // Chrome 扩展导出固定使用 index.html
  const htmlPath = path.join(sourcePath, 'index.html');
  
  if (!fs.existsSync(htmlPath)) {
    throw new Error(`找不到 index.html 文件: ${htmlPath}`);
  }
  
  const html = fs.readFileSync(htmlPath, 'utf8');
  
  const headContent = extractHeadContent(html);
  const bodyContent = extractBodyContent(html);
  
  // 从外部 CSS 文件提取字体
  const externalCSSPath = path.join(sourcePath, 'style.css');
  let fonts = [];
  if (fs.existsSync(externalCSSPath)) {
    fonts = extractFontsFromCSS(externalCSSPath);
    if (fonts.length > 0) {
      log(`  ✓ 从 style.css 提取了 ${fonts.length} 个字体定义`, 'info');
    }
  }
  
  ensureDir(outputDir);
  
  // 生成组件和样式
  const componentCode = generateComponent(pageSlug, displayName, bodyContent, headContent);
  const styleCSS = generateStyleCSS(fonts, sourcePath);
  const outputTsxPath = path.join(outputDir, 'index.tsx');

  assertValidGeneratedTsx(componentCode, outputTsxPath);
  
  fs.writeFileSync(outputTsxPath, componentCode);
  fs.writeFileSync(path.join(outputDir, 'style.css'), styleCSS);
  
  // 复制静态资源
  const assetsPath = path.join(sourcePath, 'assets');
  if (fs.existsSync(assetsPath)) {
    const outputAssetsPath = path.join(outputDir, 'assets');
    
    // 复制图片
    const imagesPath = path.join(assetsPath, 'images');
    if (fs.existsSync(imagesPath)) {
      const imageCount = copyDirectory(imagesPath, path.join(outputAssetsPath, 'images'));
      if (imageCount > 0) {
        log(`  ✓ 复制了 ${imageCount} 个图片文件`, 'info');
      }
    }
    
    // 复制本地字体
    const localFonts = fonts.filter(f => !f.isCDN);
    if (localFonts.length > 0) {
      const fontsPath = path.join(assetsPath, 'fonts');
      if (fs.existsSync(fontsPath)) {
        const fontCount = copyDirectory(fontsPath, path.join(outputAssetsPath, 'fonts'));
        log(`  ✓ 复制了 ${fontCount} 个字体文件`, 'info');
      }
    }
    
    // 统计 CDN 字体
    const cdnFonts = fonts.filter(f => f.isCDN);
    if (cdnFonts.length > 0) {
      log(`  ✓ 保留了 ${cdnFonts.length} 个 CDN 字体链接`, 'info');
    }
  }
  
  // 复制参考文件（如果存在）
  const filesToCopy = ['screenshot.png', 'content.md', 'theme.json'];
  filesToCopy.forEach(filename => {
    const srcFile = path.join(sourcePath, filename);
    if (fs.existsSync(srcFile)) {
      const destFile = path.join(outputDir, filename);
      fs.copyFileSync(srcFile, destFile);
      log(`  ✓ 复制了 ${filename}`, 'info');
    }
  });
  
  log(`页面转换完成: ${pageSlug}`, 'info');
}

/**
 * 检测项目类型（仅支持 Chrome 扩展导出）
 */
function detectProjectType(sourcePath) {
  const items = fs.readdirSync(sourcePath);
  
  // 检查是否为 Chrome 扩展导出格式（有 index.html）
  if (items.includes('index.html')) {
    return { type: 'chrome-export', prototypes: [{ name: 'index', path: sourcePath }] };
  }
  
  throw new Error('未找到 index.html 文件，请确认这是 Chrome 扩展导出的项目');
}

/**
 * 主函数
 */
async function main() {
  const args = process.argv.slice(2);
  
  if (args.length === 0 || args[0] === '--help') {
    console.log(`
Chrome 扩展导出转换器

使用方法:
  node scripts/chrome-export-converter.mjs <source-dir> [output-name] [display-name]
  node scripts/chrome-export-converter.mjs <source-dir> --name <output-name> --display-name <display-name> --target-dir <relative-dir>

参数说明:
  source-dir   : Chrome 扩展导出的目录（包含 index.html）
  output-name  : 输出页面名称（可选，默认使用目录名）
  display-name : 页面显示名（可选，写入 index.tsx 的 @name）
  target-dir   : 输出到 src/prototypes 下的相对目录（可选）

示例:
  node scripts/chrome-export-converter.mjs ".drafts/my-export" my-page
  node scripts/chrome-export-converter.mjs ".drafts/my-export" my-page "登录页"
  node scripts/chrome-export-converter.mjs ".drafts/my-export" --name my-page --target-dir grouped/login-page
    `);
    process.exit(0);
  }

  const flags = {};
  const positionals = [];
  for (let i = 0; i < args.length; i += 1) {
    const token = args[i];
    if (token === '--name' || token === '--display-name' || token === '--target-dir') {
      const next = args[i + 1];
      if (typeof next === 'string' && next) {
        flags[token] = next;
        i += 1;
      } else {
        flags[token] = '';
      }
      continue;
    }
    positionals.push(token);
  }

  const sourceDirArg = positionals[0];
  const outputNameRaw = flags['--name'] || positionals[1] || path.basename(sourceDirArg);
  const outputName = String(outputNameRaw)
    .replace(/[^a-z0-9-]/gi, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')
    .toLowerCase();
  const displayNameRaw = flags['--display-name'] ?? positionals[2];
  const displayName = (displayNameRaw !== undefined ? String(displayNameRaw).trim() : '') || outputName;
  if (displayNameRaw !== undefined) {
    const trimmedDisplayName = String(displayNameRaw).trim();
    if (!trimmedDisplayName || trimmedDisplayName.length > 200) {
      throw new Error('displayName 长度必须在 1-200 字符');
    }
  }

  const requestedTargetDir = normalizeRelativeDir(flags['--target-dir'] || outputName);
  if (!isSafeRelativeDir(requestedTargetDir)) {
    throw new Error('target-dir 必须是 src/prototypes 下的安全相对路径');
  }

  const sourcePath = path.resolve(CONFIG.projectRoot, sourceDirArg);
  const outputDir = path.resolve(CONFIG.pagesDir, requestedTargetDir);
  const resolvedPagesDir = path.resolve(CONFIG.pagesDir);
  if (outputDir === resolvedPagesDir || !outputDir.startsWith(`${resolvedPagesDir}${path.sep}`)) {
    throw new Error('target-dir 超出 src/prototypes 目录范围');
  }
  
  if (!fs.existsSync(sourcePath)) {
    log(`错误: 找不到目录 ${sourcePath}`, 'error');
    process.exit(1);
  }
  
  try {
    log('开始转换 Chrome 扩展导出...', 'info');
    
    const { type, prototypes } = detectProjectType(sourcePath);
    log(`项目类型: ${type}`, 'info');
    
    convertPage(prototypes[0].path, outputDir, outputName, displayName);
    log('✅ 转换完成！', 'info');
    log(`📁 页面位置: ${outputDir}`, 'info');
    
  } catch (error) {
    log(`转换失败: ${error.message}`, 'error');
    console.error(error);
    process.exit(1);
  }
}

if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
  main();
}

export {
  convertCommonAttributesToJSX,
  convertHtmlToJSX,
  convertStyleToJSX,
  extractBodyContent,
  generateComponent,
};
