#!/usr/bin/env node

/**
 * Contract Consistency Test
 *
 * 验证一个由 Pipeline 生成的项目中：
 *   1. Schema 字段 100% 匹配 Contract 定义
 *   2. Types 字段名（camelCase）100% 匹配 Contract 的 fieldMapping
 *   3. Routes 的 DTO 类型 100% 匹配 Contract
 *   4. Services 的 SQL 列名 100% 匹配 Contract
 *   5. Tests 使用的字段名 100% 匹配 Contract
 *
 * 验证失败则 exit(1)，成功 exit(0)。
 * 输出 contract-consistency-report.md。
 *
 * Usage:
 *   node scripts/contract-consistency-test.mjs --project <dir> [--prd <path>] [--arch <path>]
 *
 * Options:
 *   --project <dir>  项目根目录
 *   --prd <path>     PRD JSON 文件路径（用于生成 Contract）
 *   --arch <path>    Architecture JSON 文件路径（用于生成 Contract）
 *   --help           显示帮助
 */

import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { createContract, toCamel, toSnake } from "./model-contract.mjs";

const __dirname = dirname(resolve(fileURLToPath(import.meta.url)));
const WORKSPACE = resolve(__dirname, "..");

// ═══════════════════════════════════════════════════════
// 1. Test Infrastructure
// ═══════════════════════════════════════════════════════

class ContractConsistencyTest {
  constructor(contract) {
    this.contract = contract;
    this.failures = [];
    this.warnings = [];
    this.checks = { total: 0, passed: 0, failed: 0 };
  }

  fail(section, message) {
    this.failures.push({ section, message });
    this.checks.failed++;
    this.checks.total++;
  }

  pass() {
    this.checks.passed++;
    this.checks.total++;
  }

  /**
   * Recursively find files matching a glob in a directory.
   */
  findFiles(dir, pattern) {
    const results = [];
    if (!existsSync(dir)) return results;

    try {
      const entries = readdirSync(dir, { withFileTypes: true });
      for (const entry of entries) {
        const fullPath = resolve(dir, entry.name);
        if (entry.isDirectory()) {
          // Skip node_modules and dist
          if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".next" || entry.name === "data") continue;
          results.push(...this.findFiles(fullPath, pattern));
        } else if (pattern.test(entry.name)) {
          results.push(fullPath);
        }
      }
    } catch { /* skip */ }
    return results;
  }
}

// ═══════════════════════════════════════════════════════
// 2. Check 1: Schema → Contract
// ═══════════════════════════════════════════════════════

function checkSchemaConsistency(test) {
  const { contract } = test;
  const entities = contract.entities;

  console.log("── Check 1: Database Schema ↔ Contract ──");

  // Find schema files
  const projectDir = process.env.PROJECT_DIR || ".";
  const schemaFiles = [
    ...test.findFiles(resolve(projectDir, "backend/src/db"), /schema\.ts$/),
    ...test.findFiles(resolve(projectDir, "src/db"), /schema\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/db"), /schema\.ts$/),
    ...test.findFiles(resolve(projectDir, "fullstack/apps/api/src/db"), /schema\.ts$/),
    ...test.findFiles(projectDir, /schema\.prisma$/),
  ];

  if (schemaFiles.length === 0) {
    test.fail("Schema", "No schema file found (src/db/schema.ts or schema.prisma)");
    return;
  }

  for (const schemaFile of schemaFiles) {
    console.log(`  Schema file: ${schemaFile}`);
    let content;
    try {
      content = readFileSync(schemaFile, "utf-8");
    } catch (e) {
      test.fail("Schema", `Cannot read ${schemaFile}: ${e.message}`);
      continue;
    }

    for (const entity of entities) {
      console.log(`  Checking entity: ${entity.name} (${entity.table})`);

      // Verify the table exists in the schema
      const tableRegex = new RegExp(`CREATE TABLE IF NOT EXISTS ${entity.table} \\(([\\s\\S]+?)\\);`, "si");
      const tableMatch = content.match(tableRegex);

      if (!tableMatch) {
        // Try without CREATE TABLE (for Prisma/simpler formats)
        const altRegex = new RegExp(`(?:CREATE TABLE|model)\\s+(?:IF NOT EXISTS\\s+)?['"]?${entity.table}['"]?`, "i");
        if (!altRegex.test(content)) {
          test.warnings.push({ section: "Schema", message: `Table '${entity.table}' not found in schema` });
          continue;
        }
      }

      // Extract column definitions
      let columns;
      if (tableMatch) {
        const body = tableMatch[1];
        columns = body
          .split(",")
          .map(c => c.trim())
          .filter(c => c.length > 0 && !c.startsWith("--"))
          .map(c => {
            // Handle DEFAULT (...) expressions with nested parens
            const cleanCol = c.replace(/DEFAULT\s*\([^)]*\)/gi, "").replace(/^\\n\s*/, "").trim();
            const parts = cleanCol.split(/\s+/);
            return { name: parts[0], raw: c };
          });
      } else {
        columns = [];
      }

      const columnNames = new Set(columns.map(c => c.name));

      for (const field of entity.fields) {
        test.pass();
        if (field.isSecret) continue; // Secret fields may not be in output types

        if (!columnNames.has(field.name)) {
          test.fail(
            `Schema/${entity.table}`,
            `Field '${field.name}' defined in Contract but MISSING from schema table '${entity.table}'`
          );
        }
      }

      // Check for extra columns not in contract (warn only)
      for (const col of columns) {
        const hasField = entity.fields.some(f => f.name === col.name);
        if (!hasField && !col.raw.toUpperCase().includes("FOREIGN KEY") &&
            !col.raw.toUpperCase().includes("PRIMARY KEY") &&
            !col.raw.toUpperCase().includes("CONSTRAINT") &&
            !col.raw.toUpperCase().includes("UNIQUE(") &&
            !col.raw.toUpperCase().includes("CHECK(") &&
            col.name !== "" && !col.raw.startsWith(")")) {
          test.warnings.push({
            section: `Schema/${entity.table}`,
            message: `Column '${col.name}' found in schema but NOT in Contract for table '${entity.table}'`
          });
        }
      }
    }
  }

  console.log(`  Schema checks: ${test.checks.passed - (test.checks.passed > 0 ? 0 : 0)} passed, ${test.checks.failed} failures`);
}

// ═══════════════════════════════════════════════════════
// 3. Check 2: Types → Contract
// ═══════════════════════════════════════════════════════

function checkTypesConsistency(test) {
  const { contract } = test;
  const projectDir = process.env.PROJECT_DIR || ".";

  console.log("── Check 2: TypeScript Types ↔ Contract ──");

  const typeFiles = [
    ...test.findFiles(resolve(projectDir, "backend/src/types"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "src/types"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/types"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/packages/shared-types"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "packages/shared-types"), /\.ts$/),
  ].filter(f => !f.endsWith(".d.ts") || f.endsWith("fastify.d.ts"));

  if (typeFiles.length === 0) {
    test.fail("Types", "No TypeScript type files found");
    return;
  }

  // Build a set of all expected camelCase field names from the contract
  const expectedFields = new Map(); // entityName → Set of field names
  for (const entity of contract.entities) {
    const fieldSet = new Set();
    for (const field of entity.fields) {
      if (field.isSecret) continue;
      const camel = toCamel(contract, field.name);
      fieldSet.add(camel);
    }
    expectedFields.set(entity.name, fieldSet);
  }

  for (const typeFile of typeFiles) {
    console.log(`  Types file: ${typeFile}`);
    let content;
    try {
      content = readFileSync(typeFile, "utf-8");
    } catch (e) {
      test.fail("Types", `Cannot read ${typeFile}: ${e.message}`);
      continue;
    }

    // For each entity, check its interface exists and has correct fields
    for (const entity of contract.entities) {
      const expected = expectedFields.get(entity.name);
      if (!expected) continue;

      // Find the interface declaration
      const ifaceRegex = new RegExp(`(?:export\\s+)?interface\\s+${entity.name}\\s*(?:extends\\s+[^{]+)?\\s*\\{([^}]*)\\}`, "s");
      const match = content.match(ifaceRegex);

      if (!match) {
        // Entity types might not be directly in this file (e.g. in shared-types)
        continue;
      }

      const body = match[1];
      const fieldLines = body
        .split("\n")
        .map(l => l.trim())
        .filter(l => l.length > 0 && !l.startsWith("//"));

      const foundFields = new Set();
      for (const line of fieldLines) {
        // Extract field name: "  username: string;" or "  username?: string;"
        const fieldMatch = line.match(/^\s*(\w+)(\?)?\s*:/);
        if (fieldMatch) {
          foundFields.add(fieldMatch[1]);
        }
      }

      // Check each expected field exists
      for (const fieldName of expected) {
        test.pass();
        if (!foundFields.has(fieldName)) {
          test.fail(
            `Types/${entity.name}`,
            `Field '${fieldName}' (camelCase) expected in interface '${entity.name}' but MISSING from types file`
          );
        }
      }

      // Warn about extra fields
      for (const found of foundFields) {
        if (!expected.has(found)) {
          test.warnings.push({
            section: `Types/${entity.name}`,
            message: `Field '${found}' found in interface '${entity.name}' but NOT in Contract`
          });
        }
      }
    }
  }

  // Also check that all entities exist as interfaces in the types
  // Find ALL interface names declared
  for (const typeFile of typeFiles) {
    let content;
    try { content = readFileSync(typeFile, "utf-8"); } catch { continue; }
    for (const entity of contract.entities) {
      const ifaceRegex = new RegExp(`interface\\s+${entity.name}\\s*(?:extends|\\{)`, "s");
      if (!ifaceRegex.test(content)) {
        // Check if entity's table is users — User is a special type
        if (entity.table !== "users") {
          // It might be in another type file; we accumulate all
          continue;
        }
      }
    }
  }
}

// ═══════════════════════════════════════════════════════
// 4. Check 3: Routes → Contract
// ═══════════════════════════════════════════════════════

function checkRoutesConsistency(test) {
  const { contract } = test;
  const projectDir = process.env.PROJECT_DIR || ".";

  console.log("── Check 3: Route DTO Types ↔ Contract ──");

  const routeFiles = [
    ...test.findFiles(resolve(projectDir, "src/routes"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/routes"), /\.ts$/),
  ];

  if (routeFiles.length === 0) {
    test.warnings.push({ section: "Routes", message: "No route files found, skipping route checks" });
    return;
  }

  for (const routeFile of routeFiles) {
    console.log(`  Route file: ${routeFile}`);
    let content;
    try {
      content = readFileSync(routeFile, "utf-8");
    } catch (e) { continue; }

    // Check auth routes use contract auth config
    // Check CRUD routes use correct entity field names
    for (const entity of contract.entities) {
      if (entity.table === "users") continue; // Skip User — handled by auth routes

      const routeName = entity.table;
      if (routeFile.includes(routeName) || routeFile.endsWith(`${routeName}.ts`)) {
        // Verify CreateInput and UpdateInput types are used
        const pascalName = entity.name;

        const hasCreateInput = new RegExp(`Create${pascalName}Input`).test(content);
        const hasUpdateInput = new RegExp(`Update${pascalName}Input`).test(content);

        if (!hasCreateInput) {
          test.fail(
            `Routes/${routeName}`,
            `Route for '${routeName}' should use Create${pascalName}Input type`
          );
        } else { test.pass(); }

        if (!hasUpdateInput) {
          test.fail(
            `Routes/${routeName}`,
            `Route for '${routeName}' should use Update${pascalName}Input type`
          );
        } else { test.pass(); }
      }
    }
  }
}

// ═══════════════════════════════════════════════════════
// 5. Check 4: Services → Contract
// ═══════════════════════════════════════════════════════

function checkServicesConsistency(test) {
  const { contract } = test;
  const projectDir = process.env.PROJECT_DIR || ".";

  console.log("── Check 4: Service SQL Columns ↔ Contract ──");

  const serviceFiles = [
    ...test.findFiles(resolve(projectDir, "src/services"), /\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/services"), /\.ts$/),
  ];

  if (serviceFiles.length === 0) {
    test.warnings.push({ section: "Services", message: "No service files found, skipping service checks" });
    return;
  }

  for (const serviceFile of serviceFiles) {
    console.log(`  Service file: ${serviceFile}`);
    let content;
    try {
      content = readFileSync(serviceFile, "utf-8");
    } catch (e) { continue; }

    // Extract SQL column names from INSERT/SELECT statements
    const insertMatches = content.matchAll(/INSERT INTO\s+(\w+)\s*\(([^)]+)\)/gi);
    for (const match of insertMatches) {
      const tableName = match[1];
      const colStr = match[2];
      const columns = colStr.split(",").map(c => c.trim().replace(/"/g, ""));

      // Find the entity for this table
      const entity = contract.entities.find(e => e.table === tableName);
      if (!entity) continue;

      const contractCols = new Set(entity.fields.filter(f => !f.isSecret).map(f => f.name));

      for (const col of columns) {
        test.pass();
        if (!contractCols.has(col)) {
          test.fail(
            `Services/${tableName}`,
            `SQL column '${col}' in INSERT statement not found in Contract for table '${tableName}'`
          );
        }
      }
    }

    // Check SELECT statements too
    const selectMatches = content.matchAll(/SELECT\s+(.+?)\s+FROM\s+(\w+)/gi);
    for (const match of selectMatches) {
      const selectStr = match[1];
      const tableName = match[2];
      const entity = contract.entities.find(e => e.table === tableName);
      if (!entity || selectStr === "*") continue;

      const selectCols = selectStr
        .split(",")
        .map(c => c.trim().split(/\s+as\s+/i)[0].trim())
        .filter(c => c.length > 0 && c !== "*");

      const contractCols = new Set(entity.fields.filter(f => !f.isSecret).map(f => f.name));

      for (const col of selectCols) {
        // Column might be aliased or use table prefix
        const bareCol = col.split(".").pop().replace(/"/g, "");
        if (!contractCols.has(bareCol)) {
          test.warnings.push({
            section: `Services/${tableName}`,
            message: `SQL selected column '${bareCol}' not found in Contract for table '${tableName}'`
          });
        }
      }
    }
  }
}

// ═══════════════════════════════════════════════════════
// 6. Check 5: Tests → Contract
// ═══════════════════════════════════════════════════════

function checkTestsConsistency(test) {
  const { contract } = test;
  const projectDir = process.env.PROJECT_DIR || ".";

  console.log("── Check 5: Test Payloads ↔ Contract ──");

  const testFiles = [
    ...test.findFiles(resolve(projectDir, "src/__tests__"), /\.test\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/__tests__"), /\.test\.ts$/),
    ...test.findFiles(projectDir, /\.test\.ts$/),
  ];

  if (testFiles.length === 0) {
    test.warnings.push({ section: "Tests", message: "No test files found, skipping test checks" });
    return;
  }

  for (const testFile of testFiles) {
    console.log(`  Test file: ${testFile}`);
    let content;
    try {
      content = readFileSync(testFile, "utf-8");
    } catch (e) { continue; }

    // Extract payload objects from .inject() calls
    const payloadRegex = /payload:\s*({[^}]+})/gs;
    const matches = [...content.matchAll(payloadRegex)];

    for (const match of matches) {
      let payloadStr = match[1];
      try {
        // Try to parse as JSON (might have template literals, so this is approximate)
        // Extract field names using regex
        const fieldRegex = /(\w+)\s*:/g;
        const payloadFields = [];
        let fm;
        while ((fm = fieldRegex.exec(payloadStr)) !== null) {
          payloadFields.push(fm[1]);
        }

        // Check if these fields are valid according to some entity
        const allContractFields = new Set();
        for (const entity of contract.entities) {
          for (const field of entity.fields) {
            if (!field.isSecret && !field.isAuto && !field.isPrimary) {
              allContractFields.add(toCamel(contract, field.name));
              allContractFields.add(field.name); // snake_case
            }
          }
        }

        for (const pf of payloadFields) {
          if (!allContractFields.has(pf)) {
            // Allow special test fields
            if (["username", "password", "nickname", "token", "authorization"].includes(pf)) {
              continue;
            }
            test.warnings.push({
              section: `Tests/${testFile}`,
              message: `Test payload field '${pf}' not found in any Contract entity`
            });
          }
        }
      } catch {
        // Non-JSON payload, skip
      }
    }

    // Check for hardcoded fake userId values
    const fakeIdMatch = content.match(/"00000000-0000-0000-0000-000000000001"/);
    if (fakeIdMatch) {
      test.warnings.push({
        section: `Tests/${testFile}`,
        message: "Hardcoded fake userId found in test. Consider using a real registered user ID."
      });
    }
  }
}

// ═══════════════════════════════════════════════════════
// 7. Check 6: Auth Configuration
// ═══════════════════════════════════════════════════════

function checkAuthConsistency(test) {
  const { contract } = test;
  const projectDir = process.env.PROJECT_DIR || ".";

  console.log("── Check 7: Auth Config ↔ Contract ──");

  const authFiles = [
    ...test.findFiles(resolve(projectDir, "src/routes"), /auth\.ts$/),
    ...test.findFiles(resolve(projectDir, "apps/api/src/routes"), /auth\.ts$/),
  ];

  if (authFiles.length === 0) {
    test.warnings.push({ section: "Auth", message: "No auth routes found" });
    return;
  }

  for (const authFile of authFiles) {
    let content;
    try {
      content = readFileSync(authFile, "utf-8");
    } catch (e) { continue; }

    // Check registration fields
    for (const field of contract.auth.registrationFields) {
      test.pass();
      if (!content.includes(field)) {
        test.fail("Auth", `Registration field '${field}' from contract.auth.registrationFields not found in auth routes`);
      }
    }

    // Check JWT payload fields
    for (const field of contract.auth.jwtPayload) {
      test.pass();
      if (!content.includes(field)) {
        test.fail("Auth", `JWT payload field '${field}' from contract.auth.jwtPayload not found in auth routes`);
      }
    }

    // Check password policy (minLength)
    const pwPolicy = contract.auth.passwordPolicy;
    if (pwPolicy.minLength) {
      const minLenRegex = new RegExp(`password\\.length\\s*<\\s*${pwPolicy.minLength}`);
      if (minLenRegex.test(content)) {
        test.pass();
      } else {
        test.fail("Auth", `Password minLength policy (${pwPolicy.minLength}) not enforced in auth routes`);
      }
    }

    // Check password_hash column is used (not a plain password column)
    test.pass();
    if (!content.includes("password_hash") && !content.includes("passwordHash")) {
      test.fail("Auth", "Auth routes should use 'password_hash' column (not plain password)");
    }
  }
}

// ═══════════════════════════════════════════════════════
// 8. Main Runner
// ═══════════════════════════════════════════════════════

function generateReport(test, contract) {
  const lines = [];

  lines.push("# Contract Consistency Report");
  lines.push("");
  lines.push(`**Generated**: ${new Date().toISOString()}`);
  lines.push(`**Project**: ${contract.projectName}`);
  lines.push(`**Domain**: ${contract.domain}`);
  lines.push(`**Entities**: ${contract.entities.length}`);
  lines.push("");
  lines.push("## Summary");
  lines.push("");
  lines.push(`| Metric | Value |`);
  lines.push(`|--------|-------|`);
  lines.push(`| Total Checks | ${test.checks.total} |`);
  lines.push(`| Passed | ${test.checks.passed} |`);
  lines.push(`| Failed | ${test.checks.failed} |`);
  lines.push(`| Warnings | ${test.warnings.length} |`);
  lines.push(`| **Result** | **${test.checks.failed === 0 ? "✅ PASS" : "❌ FAIL"}** |`);
  lines.push("");

  if (test.failures.length > 0) {
    lines.push("## ❌ Failures");
    lines.push("");
    for (const f of test.failures) {
      lines.push(`- **${f.section}**: ${f.message}`);
    }
    lines.push("");
  }

  if (test.warnings.length > 0) {
    lines.push("## ⚠️ Warnings");
    lines.push("");
    for (const w of test.warnings) {
      lines.push(`- **${w.section}**: ${w.message}`);
    }
    lines.push("");
  }

  lines.push("## Contract Entities");
  lines.push("");
  for (const entity of contract.entities) {
    lines.push(`### ${entity.name} (\`${entity.table}\`)`);
    lines.push(`*${entity.description}*`);
    lines.push("");
    lines.push("| Field | Type | Required | Notes |");
    lines.push("|-------|------|----------|-------|");
    for (const field of entity.fields) {
      const notes = [];
      if (field.isPrimary) notes.push("PK");
      if (field.isAuto) notes.push("auto");
      if (field.isSecret) notes.push("secret");
      if (field.unique) notes.push("unique");
      if (field.enum) notes.push(`enum: ${field.enum.join("|")}`);
      if (field.defaultValue !== undefined) notes.push(`default: ${field.defaultValue}`);
      if (field.fkEntity) notes.push(`FK→${field.fkEntity}.${field.fkColumn}`);
      lines.push(`| \`${field.name}\` | ${field.type} | ${field.required ? "✓" : ""} | ${notes.join(", ")} |`);
    }
    lines.push("");
  }

  // Field mapping reference
  lines.push("## Field Mapping");
  lines.push("");
  lines.push("| snake_case | camelCase |");
  lines.push("|------------|-----------|");
  for (const [snake, camel] of Object.entries(contract.fieldMapping.snakeToCamel)) {
    if (snake !== camel) {
      lines.push(`| \`${snake}\` | \`${camel}\` |`);
    }
  }
  lines.push("");

  const report = lines.join("\n");

  // Write report
  const reportPath = resolve(WORKSPACE, "contract-consistency-report.md");
  mkdirSync(dirname(reportPath), { recursive: true });
  writeFileSync(reportPath, report);
  console.log(`\nReport written to: ${reportPath}`);

  return report;
}

async function main() {
  const args = process.argv.slice(2);
  let projectDir = null, prdPath = null, archPath = null;

  for (let i = 0; i < args.length; i++) {
    if (args[i] === "--project" && args[i + 1]) {
      projectDir = resolve(args[++i]);
    } else if (args[i] === "--prd" && args[i + 1]) {
      prdPath = resolve(args[++i]);
    } else if (args[i] === "--arch" && args[i + 1]) {
      archPath = resolve(args[++i]);
    } else if (args[i] === "--help" || args[i] === "-h") {
      console.log(`
Contract Consistency Test

Usage:
  node scripts/contract-consistency-test.mjs --project <dir> [--prd <path>] [--arch <path>]

Options:
  --project <dir>  项目根目录
  --prd <path>     PRD JSON 文件路径
  --arch <path>    Architecture JSON 文件路径
  --help           显示帮助
`);
      process.exit(0);
    }
  }

  if (!projectDir) {
    console.error("Error: --project <dir> is required. Use --help.");
    process.exit(1);
  }

  // Set global PROJECT_DIR for check functions
  process.env.PROJECT_DIR = projectDir;

  // Load PRD and Architecture if available, or create a minimal contract
  let prd = null, arch = null;

  if (prdPath && existsSync(prdPath)) {
    try {
      prd = JSON.parse(readFileSync(prdPath, "utf-8"));
    } catch (e) {
      console.error(`Warning: Failed to load PRD: ${e.message}`);
    }
  }

  if (archPath && existsSync(archPath)) {
    try {
      arch = JSON.parse(readFileSync(archPath, "utf-8"));
    } catch (e) {
      console.error(`Warning: Failed to load Architecture: ${e.message}`);
    }
  }

  // If no PRD/arch, try to derive contract from generated project
  if (!prd && !arch) {
    // Check if project has a prd.json or arch.json
    const projectPrd = resolve(projectDir, "prd.json");
    const projectArch = resolve(projectDir, "arch.json");

    if (existsSync(projectPrd)) {
      try { prd = JSON.parse(readFileSync(projectPrd, "utf-8")); } catch {}
    }
    if (existsSync(projectArch)) {
      try { arch = JSON.parse(readFileSync(projectArch, "utf-8")); } catch {}
    }
  }

  // If still no PRD, try to detect domain from project structure
  if (!prd) {
    prd = { projectName: "ContractMgmt", domain: "enterprise", features: [], pages: [], apiRequirements: [] };
  }

  // Create contract
  const contract = createContract(prd, arch);

  console.log(`\n═══ Contract Consistency Test ═══`);
  console.log(`Project: ${contract.projectName}`);
  console.log(`Domain: ${contract.domain}`);
  console.log(`Entities: ${contract.entities.map(e => e.name).join(", ")}`);
  console.log(`Project Dir: ${projectDir}\n`);

  // Initialize test
  const test = new ContractConsistencyTest(contract);

  // Run checks
  try { checkSchemaConsistency(test); } catch (e) { test.fail("Schema", `Error: ${e.message}`); }
  try { checkTypesConsistency(test); } catch (e) { test.fail("Types", `Error: ${e.message}`); }
  try { checkRoutesConsistency(test); } catch (e) { test.fail("Routes", `Error: ${e.message}`); }
  try { checkServicesConsistency(test); } catch (e) { test.fail("Services", `Error: ${e.message}`); }
  try { checkTestsConsistency(test); } catch (e) { test.fail("Tests", `Error: ${e.message}`); }
  try { checkAuthConsistency(test); } catch (e) { test.fail("Auth", `Error: ${e.message}`); }

  // Generate report
  const report = generateReport(test, contract);

  // Print summary
  console.log(`\n═══ Results ═══`);
  console.log(`Total: ${test.checks.total} | Passed: ${test.checks.passed} | Failed: ${test.checks.failed} | Warnings: ${test.warnings.length}`);
  console.log(`Result: ${test.checks.failed === 0 ? "✅ PASS" : "❌ FAIL"}`);

  // Exit with appropriate code
  process.exit(test.checks.failed > 0 ? 1 : 0);
}

main();
