#!/usr/bin/env node
/**
 * Extract API Endpoints from SmartStack Controllers
 *
 * Usage:
 *   node extract-api-endpoints.ts --module users --app-path "D:/01 - projets/SmartStack.app/02-Develop"
 *
 * Output: JSON array of DocApiEndpoint objects
 */

import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';

interface DocApiEndpoint {
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
  path: string;
  handler: string;
  permission: string;
}

interface PermissionConstant {
  constantPath: string; // e.g., "Permissions.Admin.Users.View"
  value: string;        // e.g., "administration.users.read"
}

/**
 * Parse command line arguments
 */
function parseArgs(): { module: string; appPath: string } {
  const args = process.argv.slice(2);
  const moduleIndex = args.indexOf('--module');
  const appPathIndex = args.indexOf('--app-path');

  if (moduleIndex === -1 || appPathIndex === -1) {
    console.error('Usage: node extract-api-endpoints.ts --module <module-name> --app-path <path-to-app>');
    process.exit(1);
  }

  return {
    module: args[moduleIndex + 1],
    appPath: args[appPathIndex + 1],
  };
}

/**
 * Find controller file for given module
 */
async function findControllerFile(appPath: string, module: string): Promise<string | null> {
  const controllersPath = path.join(appPath, 'src/SmartStack.Api/Controllers');
  const pattern = `**/*${capitalize(module)}*Controller.cs`;

  try {
    const files = await glob(pattern, { cwd: controllersPath, absolute: true });
    return files.length > 0 ? files[0] : null;
  } catch (error) {
    console.error('Error finding controller:', error);
    return null;
  }
}

/**
 * Extract NavRoute attribute value
 * Example: [NavRoute("administration.users")] → "administration.users"
 */
function extractNavRoute(content: string): string | null {
  const navRouteRegex = /\[NavRoute\("([^"]+)"\)\]/;
  const match = content.match(navRouteRegex);
  return match ? match[1] : null;
}

/**
 * Extract HTTP method and route suffix
 * Examples:
 *   [HttpGet] → { method: 'GET', suffix: '' }
 *   [HttpPost("groups")] → { method: 'POST', suffix: '/groups' }
 *   [HttpPut("{id}")] → { method: 'PUT', suffix: '/{id}' }
 */
function extractHttpMethod(line: string): { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; suffix: string } | null {
  const methods = ['Get', 'Post', 'Put', 'Delete', 'Patch'];

  for (const method of methods) {
    // Match [HttpGet] or [HttpGet("path")]
    const regex = new RegExp(`\\[Http${method}(?:\\("([^"]+)"\\))?\\]`);
    const match = line.match(regex);

    if (match) {
      const suffix = match[1] ? `/${match[1]}` : '';
      return {
        method: method.toUpperCase() as any,
        suffix: suffix,
      };
    }
  }

  return null;
}

/**
 * Extract permission from RequirePermission attribute
 * Example: [RequirePermission(Permissions.Admin.Users.View)] → "Permissions.Admin.Users.View"
 */
function extractPermissionConstant(line: string): string | null {
  const requirePermissionRegex = /\[RequirePermission\(([^\)]+)\)\]/;
  const match = line.match(requirePermissionRegex);

  if (!match) return null;

  // Handle both single permission and params array
  const permissionArg = match[1].trim();

  // If it's a params array, take the first one
  if (permissionArg.includes(',')) {
    return permissionArg.split(',')[0].trim();
  }

  return permissionArg;
}

/**
 * Extract method/handler name from method signature
 * Example: public async Task<ActionResult> GetUsers(...) → "GetUsers"
 */
function extractHandlerName(line: string): string | null {
  const handlerRegex = /public\s+(?:async\s+)?Task.*?\s+(\w+)\s*\(/;
  const match = line.match(handlerRegex);
  return match ? match[1] : null;
}

/**
 * Load permission constants from Permissions.cs
 * Maps constant paths to their string values
 */
async function loadPermissionConstants(appPath: string): Promise<Map<string, string>> {
  const permissionsFile = path.join(
    appPath,
    'src/SmartStack.Application/Common/Authorization/Permissions.cs'
  );

  if (!fs.existsSync(permissionsFile)) {
    console.warn('Permissions.cs not found, skipping permission resolution');
    return new Map();
  }

  const content = fs.readFileSync(permissionsFile, 'utf-8');
  const map = new Map<string, string>();

  // Parse permission constant definitions
  // Example: public const string View = "administration.users.read";
  const lines = content.split('\n');
  let currentNamespace = '';

  for (const line of lines) {
    // Track nested class structure for namespace building
    const classMatch = line.match(/public\s+(?:static\s+)?class\s+(\w+)/);
    if (classMatch) {
      if (!currentNamespace) {
        currentNamespace = `Permissions.${classMatch[1]}`;
      } else {
        currentNamespace += `.${classMatch[1]}`;
      }
    }

    // Match const string declarations
    const constMatch = line.match(/public\s+const\s+string\s+(\w+)\s*=\s*"([^"]+)"/);
    if (constMatch && currentNamespace) {
      const constantName = constMatch[1];
      const value = constMatch[2];
      const fullPath = `${currentNamespace}.${constantName}`;
      map.set(fullPath, value);
    }

    // Reset namespace on closing brace (simplified - assumes proper nesting)
    if (line.trim() === '}' && currentNamespace) {
      const lastDot = currentNamespace.lastIndexOf('.');
      currentNamespace = lastDot > 0 ? currentNamespace.substring(0, lastDot) : '';
    }
  }

  return map;
}

/**
 * Resolve permission constant to its string value
 */
function resolvePermission(
  constantPath: string,
  permissionMap: Map<string, string>,
  navRoute: string
): string {
  // Direct lookup
  if (permissionMap.has(constantPath)) {
    return permissionMap.get(constantPath)!;
  }

  // Fallback: infer from constant name
  // Permissions.Admin.Users.View → administration.users.read
  const parts = constantPath.split('.');
  if (parts.length >= 3) {
    const action = parts[parts.length - 1].toLowerCase();
    const actionMap: Record<string, string> = {
      'view': 'read',
      'create': 'create',
      'update': 'update',
      'delete': 'delete',
      'assign': 'assign',
      'execute': 'execute',
      'export': 'export',
      'import': 'import',
    };

    const mappedAction = actionMap[action] || action;
    return `${navRoute}.${mappedAction}`;
  }

  // Ultimate fallback
  return `${navRoute}.unknown`;
}

/**
 * Parse controller file and extract endpoints
 */
async function extractEndpoints(
  controllerPath: string,
  permissionMap: Map<string, string>
): Promise<DocApiEndpoint[]> {
  const content = fs.readFileSync(controllerPath, 'utf-8');
  const lines = content.split('\n');
  const endpoints: DocApiEndpoint[] = [];

  // Extract NavRoute for base path
  const navRoute = extractNavRoute(content);
  if (!navRoute) {
    console.warn('NavRoute not found in controller');
    return endpoints;
  }

  // Build base API path from navRoute
  // "administration.users" → "/api/administration/users"
  const basePath = `/api/${navRoute.replace(/\./g, '/')}`;

  // Parse line by line to find endpoint methods
  let currentMethod: { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; suffix: string } | null = null;
  let currentPermission: string | null = null;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];

    // Check for HTTP method attribute
    const httpMethod = extractHttpMethod(line);
    if (httpMethod) {
      currentMethod = httpMethod;
      continue;
    }

    // Check for RequirePermission attribute
    const permissionConstant = extractPermissionConstant(line);
    if (permissionConstant) {
      currentPermission = resolvePermission(permissionConstant, permissionMap, navRoute);
      continue;
    }

    // Check for method signature (handler name)
    if (currentMethod) {
      const handlerName = extractHandlerName(line);
      if (handlerName) {
        endpoints.push({
          method: currentMethod.method,
          path: basePath + currentMethod.suffix,
          handler: handlerName,
          permission: currentPermission || `${navRoute}.unknown`,
        });

        // Reset state
        currentMethod = null;
        currentPermission = null;
      }
    }
  }

  return endpoints;
}

/**
 * Capitalize first letter
 */
function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Main execution
 */
async function main() {
  const { module, appPath } = parseArgs();

  console.error(`Extracting API endpoints for module: ${module}`);
  console.error(`App path: ${appPath}`);

  // Find controller file
  const controllerPath = await findControllerFile(appPath, module);
  if (!controllerPath) {
    console.error(`Controller not found for module: ${module}`);
    process.exit(1);
  }

  console.error(`Found controller: ${controllerPath}`);

  // Load permission constants
  const permissionMap = await loadPermissionConstants(appPath);
  console.error(`Loaded ${permissionMap.size} permission constants`);

  // Extract endpoints
  const endpoints = await extractEndpoints(controllerPath, permissionMap);

  console.error(`Extracted ${endpoints.length} endpoints`);

  // Output JSON to stdout
  console.log(JSON.stringify(endpoints, null, 2));
}

main().catch((error) => {
  console.error('Fatal error:', error);
  process.exit(1);
});
