'use server';

import { readFile, readdir } from 'node:fs/promises';
import { join, relative, basename, extname } from 'node:path';

// ============================================================
// 소스코드 조회 (기존)
// ============================================================

export async function getComponentSource(filePath: string): Promise<{ source: string | null; error: string | null }> {
  if (!filePath.startsWith('src/components/') || !filePath.endsWith('.tsx')) {
    return { source: null, error: 'Invalid file path' };
  }

  try {
    const fullPath = join(process.cwd(), filePath);
    const source = await readFile(fullPath, 'utf-8');
    return { source, error: null };
  } catch {
    return { source: null, error: 'File not found' };
  }
}

// ============================================================
// 컴포넌트 레지스트리 실시간 스캔
// ============================================================

export interface ComponentEntry {
  name: string;
  fileName: string;
  filePath: string;
  tier: string;
  category: string;
  subcategory: string | null;
  exportType: 'default' | 'named' | 'both' | 'none';
  hasProps: boolean;
  propsName: string | null;
  isClientComponent: boolean;
  lineCount: number;
  imports: string[];   // 이 컴포넌트가 사용하는 다른 컴포넌트 filePath[]
  usedBy: string[];    // 이 컴포넌트를 사용하는 컴포넌트 filePath[]
}

interface CategorySummary {
  tier: string;
  category: string;
  count: number;
}

export interface RegistryData {
  generatedAt: string;
  totalCount: number;
  categories: CategorySummary[];
  components: ComponentEntry[];
}

// Tier mapping
const TIER_MAP: Record<string, string> = {
  ui: 'ui',
  atoms: 'atoms',
  molecules: 'molecules',
  organisms: 'organisms',
  common: 'common',
  layout: 'layout',
  dev: 'dev',
};

// Skip patterns
const SKIP_FILES = new Set([
  'index.ts', 'index.tsx', 'actions.ts', 'actions.tsx',
  'types.ts', 'types.tsx', 'utils.ts', 'utils.tsx',
  'constants.ts', 'constants.tsx', 'schema.ts', 'schema.tsx',
]);

const SKIP_PATTERNS = [
  /\.test\./,
  /\.spec\./,
  /\.stories\./,
  /^use[A-Z].*\.ts$/, // hooks (useXxx.ts only, not .tsx)
];

function shouldSkip(fileName: string): boolean {
  if (SKIP_FILES.has(fileName)) return true;
  return SKIP_PATTERNS.some(p => p.test(fileName));
}

function getTier(relPath: string): string {
  const firstDir = relPath.split('/')[0];
  return TIER_MAP[firstDir] || 'domain';
}

function getCategory(relPath: string): string {
  return relPath.split('/')[0];
}

function getSubcategory(relPath: string): string | null {
  const parts = relPath.split('/');
  return parts.length > 2 ? parts[1] : null;
}

function extractComponentImports(content: string): string[] {
  const regex = /from\s+['"]@\/components\/([^'"]+)['"]/g;
  const imports: string[] = [];
  let match;
  while ((match = regex.exec(content)) !== null) {
    imports.push(match[1]); // "ui/button", "organisms/DataTable" 등
  }
  return imports;
}

function extractComponentInfo(content: string, fileName: string) {
  const isClient = /^['"]use client['"];?/m.test(content);

  let name: string | null = null;
  let exportType: 'default' | 'named' | 'both' | 'none' = 'none';

  const defaultFuncMatch = content.match(/export\s+default\s+function\s+([A-Z]\w*)/);
  const defaultConstMatch = content.match(/export\s+default\s+([A-Z]\w*)/);
  const namedFuncMatches = [...content.matchAll(/export\s+(?:async\s+)?function\s+([A-Z]\w*)/g)];
  const namedConstMatches = [...content.matchAll(/export\s+(?:const|let)\s+([A-Z]\w*)/g)];

  const hasDefault = !!(defaultFuncMatch || defaultConstMatch);
  const namedExports = [
    ...namedFuncMatches.map(m => m[1]),
    ...namedConstMatches.map(m => m[1]),
  ].filter((n): n is string => n !== undefined);

  if (defaultFuncMatch) {
    name = defaultFuncMatch[1];
  } else if (defaultConstMatch) {
    name = defaultConstMatch[1];
  }

  const hasNamed = namedExports.length > 0;

  if (hasDefault && hasNamed) {
    exportType = 'both';
  } else if (hasDefault) {
    exportType = 'default';
  } else if (hasNamed) {
    exportType = 'named';
    name = namedExports[0];
  }

  if (!name) {
    const base = basename(fileName, extname(fileName));
    name = base
      .split(/[-_]/)
      .map(s => s.charAt(0).toUpperCase() + s.slice(1))
      .join('');
  }

  const hasProps = /(?:interface|type)\s+\w*Props/.test(content);
  const propsNameMatch = content.match(/(?:interface|type)\s+(\w*Props)/);
  const propsName = propsNameMatch ? propsNameMatch[1] : null;

  const lineCount = content.split('\n').length;
  const componentImports = extractComponentImports(content);

  return { name, exportType, hasProps, propsName, isClientComponent: isClient, lineCount, componentImports };
}

async function scanDirectory(dir: string, componentsRoot: string): Promise<ComponentEntry[]> {
  const entries = await readdir(dir, { withFileTypes: true });
  const results: ComponentEntry[] = [];

  for (const entry of entries) {
    const fullPath = join(dir, entry.name);

    if (entry.isDirectory()) {
      results.push(...await scanDirectory(fullPath, componentsRoot));
    } else if (entry.isFile() && entry.name.endsWith('.tsx')) {
      if (shouldSkip(entry.name)) continue;

      const relPath = relative(componentsRoot, fullPath);
      const content = await readFile(fullPath, 'utf-8');
      const info = extractComponentInfo(content, entry.name);

      results.push({
        name: info.name,
        fileName: entry.name,
        filePath: `src/components/${relPath}`,
        tier: getTier(relPath),
        category: getCategory(relPath),
        subcategory: getSubcategory(relPath),
        exportType: info.exportType,
        hasProps: info.hasProps,
        propsName: info.propsName,
        isClientComponent: info.isClientComponent,
        lineCount: info.lineCount,
        imports: info.componentImports,
        usedBy: [],
      });
    }
  }

  return results;
}

function buildCategories(components: ComponentEntry[]): CategorySummary[] {
  const map = new Map<string, CategorySummary>();
  for (const comp of components) {
    const key = `${comp.tier}::${comp.category}`;
    if (!map.has(key)) {
      map.set(key, { tier: comp.tier, category: comp.category, count: 0 });
    }
    map.get(key)!.count++;
  }
  return [...map.values()].sort((a, b) =>
    a.tier.localeCompare(b.tier) || a.category.localeCompare(b.category)
  );
}

/**
 * import 경로 → filePath 매핑으로 양방향 관계(imports/usedBy) 구축
 */
function buildRelationships(components: ComponentEntry[]): void {
  // 경로 조회 맵: import 경로 변형 → filePath
  const pathLookup = new Map<string, string>();

  for (const comp of components) {
    // "src/components/ui/button.tsx" → 여러 매칭 키 등록
    const rel = comp.filePath.replace(/^src\/components\//, ''); // "ui/button.tsx"
    const withoutExt = rel.replace(/\.tsx$/, '');                // "ui/button"

    pathLookup.set(withoutExt, comp.filePath);
    pathLookup.set(rel, comp.filePath);

    // 디렉토리/파일명 패턴: "organisms/DataTable/DataTable" → filePath
    const parts = withoutExt.split('/');
    if (parts.length >= 2) {
      const dirName = parts[parts.length - 2];
      const fileName = parts[parts.length - 1];
      // "organisms/DataTable" → filePath (barrel 패턴 대응)
      if (dirName === fileName) {
        const barrelPath = parts.slice(0, -1).join('/');
        if (!pathLookup.has(barrelPath)) {
          pathLookup.set(barrelPath, comp.filePath);
        }
      }
    }
  }

  // 각 컴포넌트의 raw imports → resolved filePath
  for (const comp of components) {
    const resolved: string[] = [];
    for (const rawImport of comp.imports) {
      // rawImport: "ui/button", "organisms/DataTable/DataTable" 등
      const found = pathLookup.get(rawImport);
      if (found && found !== comp.filePath) {
        resolved.push(found);
      }
    }
    comp.imports = [...new Set(resolved)];
  }

  // 역방향: usedBy 구축
  const usedByMap = new Map<string, Set<string>>();
  for (const comp of components) {
    for (const imp of comp.imports) {
      if (!usedByMap.has(imp)) usedByMap.set(imp, new Set());
      usedByMap.get(imp)!.add(comp.filePath);
    }
  }
  for (const comp of components) {
    const set = usedByMap.get(comp.filePath);
    comp.usedBy = set ? [...set] : [];
  }
}

/**
 * 컴포넌트 레지스트리 실시간 스캔
 *
 * src/components/ 하위 .tsx 파일을 스캔하여 레지스트리 데이터를 반환합니다.
 * 페이지 방문 시마다 최신 상태를 반영합니다.
 */
export async function scanComponentRegistry(): Promise<RegistryData> {
  const componentsRoot = join(process.cwd(), 'src', 'components');

  const components = await scanDirectory(componentsRoot, componentsRoot);
  buildRelationships(components);
  components.sort((a, b) =>
    a.tier.localeCompare(b.tier) || a.category.localeCompare(b.category) || a.name.localeCompare(b.name)
  );

  return {
    generatedAt: new Date().toISOString(),
    totalCount: components.length,
    categories: buildCategories(components),
    components,
  };
}
