import { readFile, writeFile, mkdir, access, constants } from 'fs/promises';
import { existsSync } from 'fs';
import { join } from 'path';
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from 'crypto';
import type { NocoDBSettings, SMTPConfig } from '~/utils/types';

interface AdminConfiguration {
  nocodb: NocoDBSettings;
  recaptcha?: {
    siteKey: string;
    secretKey: string; // Will be encrypted
  };
  registration?: {
    membershipFee: number;
    iban: string;
    accountHolder: string;
  };
  smtp?: {
    host: string;
    port: number;
    secure: boolean;
    username: string;
    password: string; // Will be encrypted
    fromAddress: string;
    fromName: string;
  };
  lastUpdated: string;
  updatedBy: string;
}

interface EffectiveNocoDB {
  url: string;
  token: string;
  baseId: string;
  tables: { 
    members: string;
    events: string;
    rsvps: string;
    [tableName: string]: string; 
  };
}

// Support both Docker (/app/data) and local development (./data) paths
const getConfigDir = () => {
  // Check if running in Docker container
  if (process.env.NODE_ENV === 'production' || existsSync('/app/data')) {
    return '/app/data';
  }
  // Local development
  return './data';
};

const CONFIG_DIR = getConfigDir();
const CONFIG_FILE = join(CONFIG_DIR, 'admin-config.json');
const BACKUP_FILE = join(CONFIG_DIR, 'admin-config.backup.json');
const LOG_FILE = join(CONFIG_DIR, 'admin-config.log');

// In-memory cache for runtime configuration
let configCache: AdminConfiguration | null = null;

/**
 * Ensure configuration directory exists
 */
async function ensureConfigDir(): Promise<void> {
  try {
    await access(CONFIG_DIR, constants.F_OK);
  } catch {
    await mkdir(CONFIG_DIR, { recursive: true });
    console.log(`[admin-config] Created config directory: ${CONFIG_DIR}`);
  }
}

/**
 * Encrypt sensitive data using the app's encryption key
 */
function encryptSensitiveData(data: string): string {
  const runtimeConfig = useRuntimeConfig();
  const encryptionKey = runtimeConfig.encryptionKey;
  
  if (!encryptionKey || encryptionKey.length < 16) {
    console.warn('[admin-config] No encryption key found or key too short, storing data as-is');
    return data;
  }

  try {
    // Use AES-256-GCM for modern encryption
    const algorithm = 'aes-256-gcm';
    const iv = randomBytes(16);
    const salt = randomBytes(16);
    
    // Derive key from the encryption key
    const key = pbkdf2Sync(encryptionKey, salt, 100000, 32, 'sha256');
    
    const cipher = createCipheriv(algorithm, key, iv);
    let encrypted = cipher.update(data, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    // Combine salt, iv, authTag, and encrypted data
    const combined = salt.toString('hex') + ':' + iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted;
    return combined;
  } catch (error) {
    console.error('[admin-config] Encryption failed:', error);
    return data;
  }
}

/**
 * Decrypt sensitive data using the app's encryption key
 */
function decryptSensitiveData(encryptedData: string): string {
  const runtimeConfig = useRuntimeConfig();
  const encryptionKey = runtimeConfig.encryptionKey;
  
  if (!encryptionKey || encryptionKey.length < 16) {
    return encryptedData;
  }

  try {
    // Check if data contains our modern format with colons
    if (!encryptedData.includes(':')) {
      // Legacy data, return as-is
      console.warn('[admin-config] Legacy encrypted data format detected, returning as-is');
      return encryptedData;
    }
    
    const parts = encryptedData.split(':');
    if (parts.length !== 4) {
      throw new Error('Invalid encrypted data format');
    }
    
    const [saltHex, ivHex, authTagHex, encrypted] = parts;
    const salt = Buffer.from(saltHex, 'hex');
    const iv = Buffer.from(ivHex, 'hex');
    const authTag = Buffer.from(authTagHex, 'hex');
    
    // Derive key from the encryption key
    const key = pbkdf2Sync(encryptionKey, salt, 100000, 32, 'sha256');
    
    const algorithm = 'aes-256-gcm';
    const decipher = createDecipheriv(algorithm, key, iv);
    decipher.setAuthTag(authTag);
    
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  } catch (error) {
    console.error('[admin-config] Decryption failed:', error);
    return encryptedData;
  }
}

/**
 * Log configuration changes for audit trail
 */
async function logConfigChange(action: string, user: string, details?: any): Promise<void> {
  const logEntry = {
    timestamp: new Date().toISOString(),
    action,
    user,
    details: details || {}
  };

  try {
    await ensureConfigDir();
    const logLine = JSON.stringify(logEntry) + '\n';
    await writeFile(LOG_FILE, logLine, { flag: 'a' });
  } catch (error) {
    console.error('[admin-config] Failed to write log:', error);
  }
}

/**
 * Create backup of current configuration before changes
 */
async function createBackup(): Promise<void> {
  try {
    await access(CONFIG_FILE, constants.F_OK);
    const currentConfig = await readFile(CONFIG_FILE, 'utf-8');
    await writeFile(BACKUP_FILE, currentConfig, 'utf-8');
    console.log('[admin-config] Configuration backup created');
  } catch (error) {
    // No existing config to backup
    console.log('[admin-config] No existing config to backup');
  }
}

/**
 * Load configuration from file
 */
export async function loadAdminConfig(): Promise<AdminConfiguration | null> {
  try {
    await ensureConfigDir();
    const configData = await readFile(CONFIG_FILE, 'utf-8');
    const config: AdminConfiguration = JSON.parse(configData);
    
    // Decrypt sensitive data
    if (config.nocodb.apiKey) {
      config.nocodb.apiKey = decryptSensitiveData(config.nocodb.apiKey);
    }
    if (config.recaptcha?.secretKey) {
      config.recaptcha.secretKey = decryptSensitiveData(config.recaptcha.secretKey);
    }
    if (config.smtp?.password) {
      config.smtp.password = decryptSensitiveData(config.smtp.password);
    }
    
    console.log('[admin-config] Configuration loaded from file');
    configCache = config;
    
    // Update global nocodb configuration
    try {
      const { setGlobalNocoDBConfig } = await import('./nocodb');
      setGlobalNocoDBConfig(getEffectiveNocoDBConfig());
    } catch (error) {
      console.error('[admin-config] Failed to update global configuration:', error);
    }
    
    return config;
  } catch (error) {
    console.log('[admin-config] No configuration file found, using defaults');
    configCache = null;
    return null;
  }
}

/**
 * Save configuration to file
 */
export async function saveAdminConfig(config: NocoDBSettings, updatedBy: string): Promise<void> {
  try {
    await ensureConfigDir();
    await createBackup();
    
    // Prepare configuration with encrypted sensitive data
    const adminConfig: AdminConfiguration = {
      nocodb: {
        ...config,
        apiKey: config.apiKey ? encryptSensitiveData(config.apiKey) : ''
      },
      lastUpdated: new Date().toISOString(),
      updatedBy
    };
    
    const configJson = JSON.stringify(adminConfig, null, 2);
    await writeFile(CONFIG_FILE, configJson, 'utf-8');
    
    // Update cache with unencrypted data for runtime use
    configCache = {
      ...adminConfig,
      nocodb: { ...config } // Keep original unencrypted data in cache
    };
    
    console.log('[admin-config] Configuration saved to file');
    await logConfigChange('CONFIG_SAVED', updatedBy, { url: config.url, baseId: config.baseId });
    
    // Update global nocodb configuration immediately
    try {
      const { setGlobalNocoDBConfig } = await import('./nocodb');
      setGlobalNocoDBConfig(getEffectiveNocoDBConfig());
      console.log('[admin-config] Global configuration updated immediately');
    } catch (error) {
      console.error('[admin-config] Failed to update global configuration after save:', error);
    }
    
  } catch (error) {
    console.error('[admin-config] Failed to save configuration:', error);
    await logConfigChange('CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(error) });
    throw error;
  }
}

/**
 * Get effective NocoDB configuration (file config merged with env defaults)
 */
export function getEffectiveNocoDBConfig(): EffectiveNocoDB {
  const runtimeConfig = useRuntimeConfig();
  
  // Start with environment variables as defaults
  const envConfig = {
    url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
    token: runtimeConfig.nocodb?.token || '',
    baseId: runtimeConfig.nocodb?.baseId || '',
    tables: { 
      members: 'members-table-id',
      events: (runtimeConfig.nocodb as any)?.eventsTableId || '',
      rsvps: (runtimeConfig.nocodb as any)?.rsvpTableId || ''
    } // Default table mapping
  };
  
  // Override with file configuration if available
  if (configCache?.nocodb) {
    return {
      url: configCache.nocodb.url || envConfig.url,
      token: configCache.nocodb.apiKey || envConfig.token,
      baseId: configCache.nocodb.baseId || envConfig.baseId,
      tables: configCache.nocodb.tables || envConfig.tables
    };
  }
  
  return envConfig;
}

/**
 * Set effective NocoDB configuration (updates cache and saves to file)
 */
export async function setEffectiveNocoDBConfig(config: EffectiveNocoDB): Promise<void> {
  try {
    await ensureConfigDir();
    await createBackup();
    
    // Get current config or create new one
    const currentConfig = configCache || await loadAdminConfig() || {
      nocodb: { url: '', apiKey: '', baseId: '', tables: { members: '', events: '', rsvps: '' } },
      lastUpdated: new Date().toISOString(),
      updatedBy: 'system'
    };
    
    // Update with new configuration
    const updatedConfig: AdminConfiguration = {
      ...currentConfig,
      nocodb: {
        url: config.url,
        apiKey: config.token,
        baseId: config.baseId,
        tables: config.tables
      },
      lastUpdated: new Date().toISOString(),
      updatedBy: 'admin-api'
    };
    
    // Encrypt sensitive data for storage
    const configForStorage = {
      ...updatedConfig,
      nocodb: {
        ...updatedConfig.nocodb,
        apiKey: updatedConfig.nocodb.apiKey ? encryptSensitiveData(updatedConfig.nocodb.apiKey) : ''
      }
    };
    
    const configJson = JSON.stringify(configForStorage, null, 2);
    await writeFile(CONFIG_FILE, configJson, 'utf-8');
    
    // Update cache with unencrypted data for runtime use
    configCache = updatedConfig;
    
    console.log('[admin-config] Effective NocoDB configuration updated');
    
    // Update global nocodb configuration immediately
    try {
      const { setGlobalNocoDBConfig } = await import('./nocodb');
      setGlobalNocoDBConfig(config);
      console.log('[admin-config] Global configuration updated immediately');
    } catch (error) {
      console.error('[admin-config] Failed to update global configuration after save:', error);
    }
    
    await logConfigChange('EFFECTIVE_CONFIG_UPDATED', 'admin-api', { 
      url: config.url, 
      baseId: config.baseId,
      tables: Object.keys(config.tables)
    });
    
  } catch (error) {
    console.error('[admin-config] Failed to set effective NocoDB configuration:', error);
    await logConfigChange('EFFECTIVE_CONFIG_UPDATE_FAILED', 'admin-api', { 
      error: error instanceof Error ? error.message : String(error) 
    });
    throw error;
  }
}

/**
 * Get current configuration for display (with masked sensitive data)
 */
export async function getCurrentConfig(): Promise<NocoDBSettings> {
  const config = configCache || await loadAdminConfig();
  const runtimeConfig = useRuntimeConfig();
  
  if (config?.nocodb) {
    return {
      url: config.nocodb.url || runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
      apiKey: config.nocodb.apiKey ? '••••••••••••••••' : '',
      baseId: config.nocodb.baseId || runtimeConfig.nocodb?.baseId || '',
      tables: config.nocodb.tables || { 
        members: 'members-table-id',
        events: '',
        rsvps: ''
      }
    };
  }
  
  // Return defaults from environment if no file config
  return {
    url: runtimeConfig.nocodb?.url || 'https://database.monacousa.org',
    apiKey: runtimeConfig.nocodb?.token ? '••••••••••••••••' : '',
    baseId: runtimeConfig.nocodb?.baseId || '',
    tables: { 
      members: 'members-table-id',
      events: '',
      rsvps: ''
    }
  };
}

/**
 * Save reCAPTCHA configuration
 */
export async function saveRecaptchaConfig(config: { siteKey: string; secretKey: string }, updatedBy: string): Promise<void> {
  try {
    await ensureConfigDir();
    await createBackup();
    
    const currentConfig = configCache || await loadAdminConfig() || {
      nocodb: { url: '', apiKey: '', baseId: '', tables: { members: '', events: '', rsvps: '' } },
      lastUpdated: new Date().toISOString(),
      updatedBy: 'system'
    };
    
    const updatedConfig: AdminConfiguration = {
      ...currentConfig,
      recaptcha: {
        siteKey: config.siteKey,
        secretKey: encryptSensitiveData(config.secretKey)
      },
      lastUpdated: new Date().toISOString(),
      updatedBy
    };
    
    const configJson = JSON.stringify(updatedConfig, null, 2);
    await writeFile(CONFIG_FILE, configJson, 'utf-8');
    
    // Update cache with unencrypted data
    configCache = {
      ...updatedConfig,
      recaptcha: {
        siteKey: config.siteKey,
        secretKey: config.secretKey
      }
    };
    
    console.log('[admin-config] reCAPTCHA configuration saved');
    await logConfigChange('RECAPTCHA_CONFIG_SAVED', updatedBy, { siteKey: config.siteKey });
    
  } catch (error) {
    console.error('[admin-config] Failed to save reCAPTCHA configuration:', error);
    await logConfigChange('RECAPTCHA_CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(error) });
    throw error;
  }
}

/**
 * Save registration configuration
 */
export async function saveRegistrationConfig(config: { membershipFee: number; iban: string; accountHolder: string }, updatedBy: string): Promise<void> {
  try {
    await ensureConfigDir();
    await createBackup();
    
    const currentConfig = configCache || await loadAdminConfig() || {
      nocodb: { url: '', apiKey: '', baseId: '', tables: { members: '', events: '', rsvps: '' } },
      lastUpdated: new Date().toISOString(),
      updatedBy: 'system'
    };
    
    const updatedConfig: AdminConfiguration = {
      ...currentConfig,
      registration: config,
      lastUpdated: new Date().toISOString(),
      updatedBy
    };
    
    const configJson = JSON.stringify(updatedConfig, null, 2);
    await writeFile(CONFIG_FILE, configJson, 'utf-8');
    
    configCache = updatedConfig;
    
    console.log('[admin-config] Registration configuration saved');
    await logConfigChange('REGISTRATION_CONFIG_SAVED', updatedBy, { membershipFee: config.membershipFee });
    
  } catch (error) {
    console.error('[admin-config] Failed to save registration configuration:', error);
    await logConfigChange('REGISTRATION_CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(error) });
    throw error;
  }
}

/**
 * Get reCAPTCHA configuration
 */
export function getRecaptchaConfig(): { siteKey: string; secretKey: string } {
  const config = configCache?.recaptcha || { siteKey: '', secretKey: '' };
  return config;
}

/**
 * Get registration configuration
 */
export function getRegistrationConfig(): { membershipFee: number; iban: string; accountHolder: string } {
  const config = configCache?.registration || {
    membershipFee: 50,
    iban: '',
    accountHolder: ''
  };
  return config;
}

/**
 * Save SMTP configuration
 */
export async function saveSMTPConfig(config: SMTPConfig, updatedBy: string): Promise<void> {
  try {
    await ensureConfigDir();
    await createBackup();
    
    const currentConfig = configCache || await loadAdminConfig() || {
      nocodb: { url: '', apiKey: '', baseId: '', tables: { members: '', events: '', rsvps: '' } },
      lastUpdated: new Date().toISOString(),
      updatedBy: 'system'
    };
    
    const updatedConfig: AdminConfiguration = {
      ...currentConfig,
      smtp: {
        ...config,
        password: encryptSensitiveData(config.password)
      },
      lastUpdated: new Date().toISOString(),
      updatedBy
    };
    
    const configJson = JSON.stringify(updatedConfig, null, 2);
    await writeFile(CONFIG_FILE, configJson, 'utf-8');
    
    // Update cache with unencrypted data
    configCache = {
      ...updatedConfig,
      smtp: { ...config } // Keep original unencrypted data in cache
    };
    
    console.log('[admin-config] SMTP configuration saved');
    await logConfigChange('SMTP_CONFIG_SAVED', updatedBy, { host: config.host, fromAddress: config.fromAddress });
    
  } catch (error) {
    console.error('[admin-config] Failed to save SMTP configuration:', error);
    await logConfigChange('SMTP_CONFIG_SAVE_FAILED', updatedBy, { error: error instanceof Error ? error.message : String(error) });
    throw error;
  }
}

/**
 * Get SMTP configuration
 */
export function getSMTPConfig(): SMTPConfig {
  const config = configCache?.smtp || {
    host: '',
    port: 587,
    secure: false,
    username: '',
    password: '',
    fromAddress: '',
    fromName: 'MonacoUSA Portal'
  };
  return config;
}

/**
 * Force reload configuration from file and update cache
 */
export async function reloadAdminConfig(): Promise<void> {
  console.log('[admin-config] Force reloading configuration from file...');
  
  try {
    // Clear cache to force reload
    configCache = null;
    
    // Load fresh from file
    await loadAdminConfig();
    
    // Update global nocodb configuration
    try {
      const { setGlobalNocoDBConfig } = await import('./nocodb');
      setGlobalNocoDBConfig(getEffectiveNocoDBConfig());
      console.log('[admin-config] Global configuration updated after reload');
    } catch (error) {
      console.error('[admin-config] Failed to update global configuration after reload:', error);
    }
    
    console.log('[admin-config] ✅ Configuration reloaded successfully');
  } catch (error) {
    console.error('[admin-config] ❌ Failed to reload configuration:', error);
    throw error;
  }
}

/**
 * Initialize configuration system on server startup
 */
export async function initAdminConfig(): Promise<void> {
  console.log('[admin-config] Initializing admin configuration system...');
  
  try {
    await ensureConfigDir();
    
    // Load configuration and initialize cache
    const config = await loadAdminConfig();
    
    if (config) {
      console.log('[admin-config] ✅ Configuration loaded from existing file');
      console.log('[admin-config] Configuration summary:', {
        nocodb: {
          url: config.nocodb.url || 'not set',
          hasApiKey: !!config.nocodb.apiKey,
          baseId: config.nocodb.baseId || 'not set'
        },
        smtp: {
          host: config.smtp?.host || 'not set',
          port: config.smtp?.port || 'not set',
          hasAuth: !!(config.smtp?.username && config.smtp?.password)
        },
        recaptcha: {
          hasSiteKey: !!config.recaptcha?.siteKey,
          hasSecretKey: !!config.recaptcha?.secretKey
        },
        registration: {
          membershipFee: config.registration?.membershipFee || 'not set',
          hasIban: !!config.registration?.iban
        }
      });
    } else {
      console.log('[admin-config] ⚠️  No existing configuration file found, using defaults');
    }
    
    console.log('[admin-config] ✅ Admin configuration system initialized');
  } catch (error) {
    console.error('[admin-config] ❌ Failed to initialize admin configuration:', error);
  }
}

/**
 * Get configuration status for health checks
 */
export function getConfigStatus(): {
  loaded: boolean;
  nocodb: boolean;
  smtp: boolean;
  recaptcha: boolean;
  registration: boolean;
} {
  const config = configCache;
  
  return {
    loaded: !!config,
    nocodb: !!(config?.nocodb?.url && config?.nocodb?.apiKey && config?.nocodb?.baseId),
    smtp: !!(config?.smtp?.host && config?.smtp?.port),
    recaptcha: !!(config?.recaptcha?.siteKey && config?.recaptcha?.secretKey),
    registration: !!(config?.registration?.membershipFee && config?.registration?.iban)
  };
}
