/**
 * Mandu Guard AST Analyzer
 *
 * TypeScript AST 기반 정밀 분석
 *
 * 정규식보다 정확한 import 추출
 * - 주석 내 import 무시
 * - 문자열 내 import 무시
 * - 복잡한 멀티라인 import 처리
 * - Type-only import 구분
 */

import type { ImportInfo } from "./types";

// ═══════════════════════════════════════════════════════════════════════════
// Token Types
// ═══════════════════════════════════════════════════════════════════════════

type TokenType =
  | "import"
  | "export"
  | "from"
  | "require"
  | "string"
  | "identifier"
  | "punctuation"
  | "keyword"
  | "comment"
  | "whitespace"
  | "newline"
  | "eof";

interface Token {
  type: TokenType;
  value: string;
  start: number;
  end: number;
  line: number;
  column: number;
}

// ═══════════════════════════════════════════════════════════════════════════
// Lexer (Tokenizer)
// ═══════════════════════════════════════════════════════════════════════════

/**
 * 간단한 TypeScript/JavaScript 토크나이저
 */
function tokenize(content: string): Token[] {
  const tokens: Token[] = [];
  let pos = 0;
  let line = 1;
  let column = 1;

  const keywords = new Set([
    "import",
    "export",
    "from",
    "as",
    "type",
    "typeof",
    "const",
    "let",
    "var",
    "function",
    "class",
    "interface",
    "enum",
    "await",
    "async",
    "default",
    "require",
  ]);

  while (pos < content.length) {
    const start = pos;
    const startLine = line;
    const startColumn = column;
    let char = content[pos];

    // Newline
    if (char === "\n") {
      pos++;
      line++;
      column = 1;
      tokens.push({
        type: "newline",
        value: "\n",
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Whitespace
    if (/\s/.test(char)) {
      while (pos < content.length && /\s/.test(content[pos]) && content[pos] !== "\n") {
        pos++;
        column++;
      }
      tokens.push({
        type: "whitespace",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Single-line comment
    if (char === "/" && content[pos + 1] === "/") {
      while (pos < content.length && content[pos] !== "\n") {
        pos++;
        column++;
      }
      tokens.push({
        type: "comment",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Multi-line comment
    if (char === "/" && content[pos + 1] === "*") {
      pos += 2;
      column += 2;
      while (pos < content.length && !(content[pos] === "*" && content[pos + 1] === "/")) {
        if (content[pos] === "\n") {
          line++;
          column = 1;
        } else {
          column++;
        }
        pos++;
      }
      pos += 2;
      column += 2;
      tokens.push({
        type: "comment",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // String (single quote)
    if (char === "'") {
      pos++;
      column++;
      while (pos < content.length && content[pos] !== "'") {
        if (content[pos] === "\\") {
          pos++;
          column++;
        }
        if (content[pos] === "\n") {
          line++;
          column = 1;
        } else {
          column++;
        }
        pos++;
      }
      pos++; // closing quote
      column++;
      tokens.push({
        type: "string",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // String (double quote)
    if (char === '"') {
      pos++;
      column++;
      while (pos < content.length && content[pos] !== '"') {
        if (content[pos] === "\\") {
          pos++;
          column++;
        }
        if (content[pos] === "\n") {
          line++;
          column = 1;
        } else {
          column++;
        }
        pos++;
      }
      pos++; // closing quote
      column++;
      tokens.push({
        type: "string",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Template literal (backtick) - simplified
    if (char === "`") {
      pos++;
      column++;
      while (pos < content.length && content[pos] !== "`") {
        if (content[pos] === "\\") {
          pos++;
          column++;
        }
        if (content[pos] === "\n") {
          line++;
          column = 1;
        } else {
          column++;
        }
        pos++;
      }
      pos++; // closing backtick
      column++;
      tokens.push({
        type: "string",
        value: content.slice(start, pos),
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Punctuation
    if (/[{}()\[\];,.*]/.test(char)) {
      pos++;
      column++;
      tokens.push({
        type: "punctuation",
        value: char,
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Identifier or keyword
    if (/[a-zA-Z_$]/.test(char)) {
      while (pos < content.length && /[a-zA-Z0-9_$]/.test(content[pos])) {
        pos++;
        column++;
      }
      const value = content.slice(start, pos);
      const type: TokenType = keywords.has(value)
        ? value === "import"
          ? "import"
          : value === "from"
          ? "from"
          : value === "require"
          ? "require"
          : "keyword"
        : "identifier";

      tokens.push({
        type,
        value,
        start,
        end: pos,
        line: startLine,
        column: startColumn,
      });
      continue;
    }

    // Skip other characters
    pos++;
    column++;
  }

  tokens.push({
    type: "eof",
    value: "",
    start: pos,
    end: pos,
    line,
    column,
  });

  return tokens;
}

// ═══════════════════════════════════════════════════════════════════════════
// AST Import Extraction
// ═══════════════════════════════════════════════════════════════════════════

/**
 * AST 기반 import 추출
 *
 * 정규식보다 정확하게 import 문을 추출
 */
export function extractImportsAST(content: string): ImportInfo[] {
  const tokens = tokenize(content);
  const imports: ImportInfo[] = [];

  let i = 0;

  // Skip whitespace, newlines, comments
  const skip = () => {
    while (
      i < tokens.length &&
      (tokens[i].type === "whitespace" ||
        tokens[i].type === "newline" ||
        tokens[i].type === "comment")
    ) {
      i++;
    }
  };

  // Get current token
  const current = () => tokens[i];

  // Check if current token matches
  const is = (type: TokenType, value?: string) => {
    const t = current();
    return t && t.type === type && (value === undefined || t.value === value);
  };

  // Consume token if matches
  const consume = (type: TokenType, value?: string) => {
    if (is(type, value)) {
      const t = current();
      i++;
      return t;
    }
    return null;
  };

  while (i < tokens.length && current().type !== "eof") {
    skip();

    // Static import: import ... from '...'
    // Dynamic import: import('...')
    if (is("import")) {
      const importToken = consume("import")!;
      skip();

      // Dynamic import: import('...')
      if (is("punctuation", "(")) {
        consume("punctuation", "(");
        skip();
        if (is("string")) {
          const pathToken = consume("string")!;
          const path = pathToken.value.slice(1, -1);
          skip();
          consume("punctuation", ")");

          imports.push({
            statement: `import('${path}')`,
            path,
            line: importToken.line,
            column: importToken.column,
            type: "dynamic",
          });
        }
        continue;
      }

      let namedImports: string[] | undefined;
      let defaultImport: string | undefined;
      let _namespaceImport: string | undefined;
      let _isTypeOnly = false;

      // Check for type-only import
      if (is("keyword", "type")) {
        consume("keyword", "type");
        skip();
        _isTypeOnly = true;
      }

      // Side-effect import: import '...'
      if (is("string")) {
        const pathToken = consume("string")!;
        const path = pathToken.value.slice(1, -1); // Remove quotes

        imports.push({
          statement: content.slice(importToken.start, pathToken.end),
          path,
          line: importToken.line,
          column: importToken.column,
          type: "static",
        });
        continue;
      }

      // Default import: import X from '...'
      if (is("identifier")) {
        defaultImport = consume("identifier")!.value;
        skip();

        // import X, { ... } from '...'
        if (is("punctuation", ",")) {
          consume("punctuation", ",");
          skip();
        }
      }

      // Namespace import: import * as X from '...'
      if (is("punctuation", "*")) {
        consume("punctuation", "*");
        skip();
        if (is("keyword", "as")) {
          consume("keyword", "as");
          skip();
          if (is("identifier")) {
            _namespaceImport = consume("identifier")!.value;
            skip();
          }
        }
      }

      // Named imports: import { X, Y } from '...'
      if (is("punctuation", "{")) {
        consume("punctuation", "{");
        skip();
        namedImports = [];

        while (!is("punctuation", "}") && !is("eof")) {
          if (is("identifier") || is("keyword")) {
            const name = current().value;
            i++;
            skip();

            // Handle 'as' alias
            if (is("keyword", "as")) {
              consume("keyword", "as");
              skip();
              if (is("identifier")) {
                consume("identifier");
                skip();
              }
            }

            namedImports.push(name);
          }

          if (is("punctuation", ",")) {
            consume("punctuation", ",");
            skip();
          } else {
            break;
          }
        }

        consume("punctuation", "}");
        skip();
      }

      // from '...'
      if (is("from")) {
        consume("from");
        skip();

        if (is("string")) {
          const pathToken = consume("string")!;
          const path = pathToken.value.slice(1, -1);

          imports.push({
            statement: content.slice(importToken.start, pathToken.end),
            path,
            line: importToken.line,
            column: importToken.column,
            type: "static",
            namedImports: namedImports?.length ? namedImports : undefined,
            defaultImport,
          });
        }
      }

      continue;
    }

    // require('...')
    if (is("require")) {
      const requireToken = consume("require")!;
      skip();

      if (is("punctuation", "(")) {
        consume("punctuation", "(");
        skip();

        if (is("string")) {
          const pathToken = consume("string")!;
          const path = pathToken.value.slice(1, -1);
          skip();
          consume("punctuation", ")");

          imports.push({
            statement: `require('${path}')`,
            path,
            line: requireToken.line,
            column: requireToken.column,
            type: "require",
          });
        }
      }
      continue;
    }

    // Move to next token
    i++;
  }

  return imports;
}

// ═══════════════════════════════════════════════════════════════════════════
// Export Analysis
// ═══════════════════════════════════════════════════════════════════════════

/**
 * Export 정보
 */
export interface ExportInfo {
  /** Export 유형 */
  type: "named" | "default" | "all" | "type";
  /** Export 이름 (named의 경우) */
  name?: string;
  /** 원본 이름 (as 사용 시) */
  originalName?: string;
  /** re-export 소스 */
  from?: string;
  /** 라인 번호 */
  line: number;
}

/**
 * Export 문 추출
 */
export function extractExportsAST(content: string): ExportInfo[] {
  const tokens = tokenize(content);
  const exports: ExportInfo[] = [];

  let i = 0;

  const skip = () => {
    while (
      i < tokens.length &&
      (tokens[i].type === "whitespace" ||
        tokens[i].type === "newline" ||
        tokens[i].type === "comment")
    ) {
      i++;
    }
  };

  const current = () => tokens[i];
  const is = (type: TokenType, value?: string) => {
    const t = current();
    return t && t.type === type && (value === undefined || t.value === value);
  };
  const consume = (type: TokenType, value?: string) => {
    if (is(type, value)) {
      const t = current();
      i++;
      return t;
    }
    return null;
  };

  while (i < tokens.length && current().type !== "eof") {
    skip();

    if (is("keyword", "export")) {
      const exportToken = consume("keyword", "export")!;
      skip();

      // export default
      if (is("keyword", "default")) {
        consume("keyword", "default");
        exports.push({
          type: "default",
          line: exportToken.line,
        });
        continue;
      }

      // export type
      if (is("keyword", "type")) {
        consume("keyword", "type");
        skip();

        if (is("punctuation", "{")) {
          // export type { ... }
          consume("punctuation", "{");
          skip();

          while (!is("punctuation", "}") && !is("eof")) {
            if (is("identifier")) {
              const name = consume("identifier")!.value;
              skip();

              let originalName: string | undefined;
              if (is("keyword", "as")) {
                consume("keyword", "as");
                skip();
                originalName = name;
                if (is("identifier")) {
                  consume("identifier");
                  skip();
                }
              }

              exports.push({
                type: "type",
                name,
                originalName,
                line: exportToken.line,
              });
            }

            if (is("punctuation", ",")) {
              consume("punctuation", ",");
              skip();
            } else {
              break;
            }
          }
        }
        continue;
      }

      // export * from '...'
      if (is("punctuation", "*")) {
        consume("punctuation", "*");
        skip();

        let from: string | undefined;
        if (is("from")) {
          consume("from");
          skip();
          if (is("string")) {
            from = consume("string")!.value.slice(1, -1);
          }
        }

        exports.push({
          type: "all",
          from,
          line: exportToken.line,
        });
        continue;
      }

      // export { ... }
      if (is("punctuation", "{")) {
        consume("punctuation", "{");
        skip();

        const names: { name: string; originalName?: string }[] = [];

        while (!is("punctuation", "}") && !is("eof")) {
          if (is("identifier")) {
            const name = consume("identifier")!.value;
            skip();

            let originalName: string | undefined;
            if (is("keyword", "as")) {
              consume("keyword", "as");
              skip();
              originalName = name;
              if (is("identifier")) {
                consume("identifier");
                skip();
              }
            }

            names.push({ name, originalName });
          }

          if (is("punctuation", ",")) {
            consume("punctuation", ",");
            skip();
          } else {
            break;
          }
        }

        consume("punctuation", "}");
        skip();

        let from: string | undefined;
        if (is("from")) {
          consume("from");
          skip();
          if (is("string")) {
            from = consume("string")!.value.slice(1, -1);
          }
        }

        for (const { name, originalName } of names) {
          exports.push({
            type: "named",
            name,
            originalName,
            from,
            line: exportToken.line,
          });
        }
        continue;
      }

      // export const/let/var/function/class
      if (
        is("keyword", "const") ||
        is("keyword", "let") ||
        is("keyword", "var") ||
        is("keyword", "function") ||
        is("keyword", "class") ||
        is("keyword", "interface") ||
        is("keyword", "enum") ||
        is("keyword", "async")
      ) {
        consume("keyword");
        skip();

        // async function
        if (is("keyword", "function")) {
          consume("keyword", "function");
          skip();
        }

        if (is("identifier")) {
          const name = consume("identifier")!.value;
          exports.push({
            type: "named",
            name,
            line: exportToken.line,
          });
        }
        continue;
      }
    }

    i++;
  }

  return exports;
}

// ═══════════════════════════════════════════════════════════════════════════
// Module Analysis
// ═══════════════════════════════════════════════════════════════════════════

/**
 * 모듈 분석 결과
 */
export interface ModuleAnalysis {
  /** Import 목록 */
  imports: ImportInfo[];
  /** Export 목록 */
  exports: ExportInfo[];
  /** Public API 여부 (index 파일) */
  isPublicAPI: boolean;
  /** 순수 타입 모듈 여부 */
  _isTypeOnly: boolean;
}

/**
 * 모듈 전체 분석
 */
export function analyzeModuleAST(content: string, filePath: string): ModuleAnalysis {
  const imports = extractImportsAST(content);
  const exports = extractExportsAST(content);

  const isPublicAPI =
    filePath.endsWith("/index.ts") ||
    filePath.endsWith("/index.tsx") ||
    filePath.endsWith("/index.js");

  // 타입만 있는 모듈인지 확인
  const hasRuntimeExport = exports.some((e) => e.type !== "type");
  const hasRuntimeImport = imports.some((i) => !i.statement.includes("import type"));
  const _isTypeOnly = !hasRuntimeExport && !hasRuntimeImport && exports.length > 0;

  return {
    imports,
    exports,
    isPublicAPI,
    _isTypeOnly,
  };
}
