/**
 * prismaModels.ts
 *
 * Shared utility for enumerating non-system Prisma business models by
 * scanning the config/prisma/ directory.
 *
 * Exported:
 *   - ModelInfo         — shape of each model entry
 *   - toCamelCase()     — PascalCase → camelCase
 *   - toKebabCase()     — PascalCase → kebab-case
 *   - readPrismaModels() — scan config/prisma/, return all business ModelInfo[]
 */

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

/**
 * Path to config/prisma/ — two levels up from server/scripts/
 * (project-root/config/prisma/)
 */
const PRISMA_CONFIG_DIR = path.resolve(
  __dirname,
  '..',
  '..',
  'config',
  'prisma',
);

/**
 * Prisma files that define system/permission models and must NOT be scanned.
 * These are already handled by static seed modules (bootstrap + permission/audit/etc.).
 */
const EXCLUDED_FILES = new Set(['system.prisma']);

// ─── Types ────────────────────────────────────────────────────────────────────

export interface ModelInfo {
  /** PascalCase — original model name, e.g. "Event" */
  name: string;
  /** camelCase  — used as page.code / resource.code, e.g. "event" */
  code: string;
  /** kebab-case — used in URL path segment, e.g. "event" */
  slug: string;
}

// ─── Name Converters ──────────────────────────────────────────────────────────

/** PascalCase → camelCase  (e.g. "Event" → "event", "ITActivityDay" → "iTActivityDay") */
export function toCamelCase(str: string): string {
  return str.charAt(0).toLowerCase() + str.slice(1);
}

/**
 * PascalCase → kebab-case
 *   "Event"         → "event"
 *   "ITActivityDay" → "it-activity-day"
 *   "GameList"      → "game-list"
 */
export function toKebabCase(str: string): string {
  return (
    str
      // Insert hyphen between a lowercase/digit and an uppercase letter
      .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
      // Insert hyphen between a run of uppercase letters and an uppercase+lowercase pair
      .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
      .toLowerCase()
  );
}

// ─── Prisma File Parser ───────────────────────────────────────────────────────

/**
 * Extract model names from a single .prisma file content.
 * Skips models annotated with `/// @IgnoreReactCode`.
 */
function extractModels(content: string): ModelInfo[] {
  const models: ModelInfo[] = [];
  const lines = content.split('\n');

  for (let i = 0; i < lines.length; i++) {
    const modelMatch = lines[i].match(/^model\s+(\w+)\s*\{/);
    if (!modelMatch) continue;

    const modelName = modelMatch[1];

    // Collect consecutive `///` comment lines immediately above this model declaration
    const annotations: string[] = [];
    let j = i - 1;
    while (j >= 0 && lines[j].trim().startsWith('///')) {
      annotations.push(lines[j]);
      j--;
    }

    // Skip models that explicitly opt out of frontend code generation
    if (annotations.some((a) => a.includes('@IgnoreReactCode'))) {
      console.log(`[prismaModels]   Skipping ${modelName} (@IgnoreReactCode)`);
      continue;
    }

    models.push({
      name: modelName,
      code: toCamelCase(modelName),
      slug: toKebabCase(modelName),
    });
  }

  return models;
}

/**
 * Scan all non-excluded .prisma files under config/prisma/ and collect
 * model definitions.
 */
export function readPrismaModels(): ModelInfo[] {
  if (!fs.existsSync(PRISMA_CONFIG_DIR)) {
    console.warn(
      `[prismaModels] Prisma config directory not found: ${PRISMA_CONFIG_DIR}`,
    );
    return [];
  }

  const files = fs
    .readdirSync(PRISMA_CONFIG_DIR)
    .filter((f) => f.endsWith('.prisma') && !EXCLUDED_FILES.has(f))
    .sort(); // deterministic order across runs

  const allModels: ModelInfo[] = [];
  for (const file of files) {
    const content = fs.readFileSync(
      path.join(PRISMA_CONFIG_DIR, file),
      'utf-8',
    );
    const models = extractModels(content);
    allModels.push(...models);
    console.log(
      `[prismaModels] Scanned ${file}: models=[${
        models.map((m) => m.name).join(', ') || '(none)'
      }]`,
    );
  }

  return allModels;
}
