/**
 * GitFlow EF Core — Migration detection and validation.
 * Self-contained: only uses node built-ins.
 */

import { readFileSync, existsSync } from 'fs';
import type { EfCoreValidation, MigrationInfo } from './types.js';

const MIGRATION_PATTERN = /Migrations?\//i;
const DESIGNER_SUFFIX = '.Designer.cs';
const SNAPSHOT_SUFFIX = 'ModelSnapshot.cs';

const DESTRUCTIVE_OPS = [
  'DropTable', 'DropColumn', 'DropIndex', 'DropForeignKey',
  'DropPrimaryKey', 'DropSchema', 'DeleteData', 'RenameTable', 'RenameColumn',
];

export function detectMigrations(changedFiles: string[]): MigrationInfo[] {
  const migrationFiles = changedFiles.filter(f => MIGRATION_PATTERN.test(f) && f.endsWith('.cs'));
  const migrations = new Map<string, MigrationInfo>();

  for (const file of migrationFiles) {
    if (file.endsWith(SNAPSHOT_SUFFIX)) continue;

    let name: string;
    if (file.endsWith(DESIGNER_SUFFIX)) {
      name = file.replace(DESIGNER_SUFFIX, '');
    } else {
      name = file.replace('.cs', '');
    }

    const baseName = name.split(/[/\\]/).pop() || name;

    if (!migrations.has(baseName)) {
      migrations.set(baseName, { name: baseName, mainFile: '' });
    }

    const info = migrations.get(baseName)!;
    if (file.endsWith(DESIGNER_SUFFIX)) {
      info.designerFile = file;
    } else {
      info.mainFile = file;
    }
  }

  const snapshotFiles = migrationFiles.filter(f => f.endsWith(SNAPSHOT_SUFFIX));
  for (const migration of migrations.values()) {
    migration.snapshotFile = snapshotFiles[0];
  }

  return Array.from(migrations.values());
}

export function validateMigration(
  migration: MigrationInfo,
  allChangedFiles: string[],
): { valid: boolean; missing: string[] } {
  const missing: string[] = [];
  if (!migration.mainFile) missing.push(`${migration.name}.cs`);
  if (!migration.designerFile) missing.push(`${migration.name}.Designer.cs`);

  const hasSnapshot = allChangedFiles.some(f => f.endsWith(SNAPSHOT_SUFFIX));
  if (!hasSnapshot) missing.push('ModelSnapshot.cs');

  return { valid: missing.length === 0, missing };
}

export function detectDestructiveOps(filePath: string): string[] {
  if (!existsSync(filePath)) return [];
  const content = readFileSync(filePath, 'utf-8');
  return DESTRUCTIVE_OPS.filter(op => content.includes(op));
}

export function validateEfCore(changedFiles: string[]): EfCoreValidation {
  const migrations = detectMigrations(changedFiles);

  if (migrations.length === 0) {
    return { detected: false, valid: true, destructive: false, migrations: [], missing: [], destructiveOps: [] };
  }

  const allMissing: string[] = [];
  const allDestructive: string[] = [];
  let allValid = true;

  for (const migration of migrations) {
    const { valid, missing } = validateMigration(migration, changedFiles);
    if (!valid) {
      allValid = false;
      allMissing.push(...missing);
    }

    if (migration.mainFile && existsSync(migration.mainFile)) {
      const ops = detectDestructiveOps(migration.mainFile);
      allDestructive.push(...ops);
    }
  }

  return {
    detected: true,
    valid: allValid,
    destructive: allDestructive.length > 0,
    migrations: migrations.map(m => m.name),
    missing: allMissing,
    destructiveOps: allDestructive,
  };
}

export function validateAppsettings(
  currentContent: string,
  previousContent?: string,
): { valid: boolean; removedSections: string[]; secrets: string[] } {
  const secrets: string[] = [];
  const removedSections: string[] = [];

  const secretPatterns = [
    { pattern: /Server=.*;.*Password=/i, label: 'Connection string with password' },
    { pattern: /["']sk-[a-zA-Z0-9]{20,}["']/i, label: 'API key' },
    { pattern: /["'][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}["']/i, label: 'GUID (potential secret)' },
  ];

  for (const { pattern, label } of secretPatterns) {
    if (pattern.test(currentContent)) secrets.push(label);
  }

  if (previousContent) {
    try {
      const current = JSON.parse(currentContent);
      const previous = JSON.parse(previousContent);
      for (const key of Object.keys(previous)) {
        if (!(key in current)) removedSections.push(key);
      }
    } catch { /* skip parse errors */ }
  }

  return { valid: secrets.length === 0 && removedSections.length === 0, removedSections, secrets };
}
