import { DEFAULT_DATABASE_URL, envBoolean, envList, envNumber, envString, loadDotenv } from '../../../packages/config/src/index.js';

loadDotenv();

export interface WorkerConfig {
  nodeEnv: string;
  isProduction: boolean;
  databaseUrl: string;
  crmBatchSize: number;
  crmPollIntervalMs: number;
  crmMaxAttempts: number;
  crmBackoffSeconds: number[];
  crmRequestTimeoutMs: number;
  crmAllowInsecureLocalhost: boolean;
  commerceBatchSize: number;
  commerceMinAgeSeconds: number;
  commerceRequestTimeoutMs: number;
  providerBillBatchSize: number;
  providerBillWorkerId: string;
  providerBillClaimStaleSeconds: number;
  platformBillingBatchSize: number;
  platformBillingDaysAhead: number;
  platformBillingDueDays: number;
  platformBillingWorkerId: string;
  platformUsageBatchSize: number;
  platformUsageWorkerId: string;
  platformUsageMonth: string;
  platformUsageOverageBatchSize: number;
  platformUsageOverageWorkerId: string;
  platformUsageOverageMonth: string;
  platformUsageOverageDueDays: number;
  platformDunningBatchSize: number;
  platformDunningWorkerId: string;
  platformDunningNotificationBatchSize: number;
  platformDunningNotificationMaxAttempts: number;
  platformDunningNotificationBackoffSeconds: number[];
  platformDunningNotificationRequestTimeoutMs: number;
  platformDunningNotificationAllowInsecureLocalhost: boolean;
  platformAuditAlertBatchSize: number;
  platformAuditAlertWorkerId: string;
  platformAuditAlertLookbackDays: number;
  platformAuditNotificationBatchSize: number;
  platformAuditNotificationMaxAttempts: number;
  platformAuditNotificationBackoffSeconds: number[];
  platformAuditNotificationRequestTimeoutMs: number;
  platformAuditNotificationAllowInsecureLocalhost: boolean;
  assetBatchSize: number;
  assetMinAgeSeconds: number;
  assetRecheckIntervalSeconds: number;
  assetRequestTimeoutMs: number;
  assetSecurityScanner: string;
  assetSecurityScanHttpEndpoint: string;
  assetSecurityScanHttpToken: string;
  assetSecurityScanHttpTimeoutMs: number;
  assetSecurityScanFailOpen: boolean;
  importBatchSize: number;
  importWorkerId: string;
  importBackoffSeconds: number[];
  publicBankSyncBatchSize: number;
  publicBankSyncCopyLimit: number;
  publicBankSyncWorkerId: string;
  publicBankSyncClaimStaleSeconds: number;
  exportBatchSize: number;
  exportWorkerId: string;
  exportBackoffSeconds: number[];
  studentSupervisionBatchSize: number;
  studentSupervisionWorkerId: string;
  studentSupervisionClaimStaleSeconds: number;
  exportLocalStorageRoot: string;
  exportPdfFontPath: string;
  storageDefaultProvider: string;
  storageDefaultBucket: string;
  storagePublicBaseUrl: string;
  storageMaxUploadBytes: number;
  storageAllowedMimePrefixes: string[];
  storageAllowedMimeTypes: string[];
  storageRequireTenantPrefix: boolean;
  aliyunOssRegion: string;
  aliyunOssEndpoint: string;
  aliyunOssAccessKeyId: string;
  aliyunOssAccessKeySecret: string;
  aliyunOssStsToken: string;
  aliyunOssInternal: boolean;
  tencentCosRegion: string;
  tencentCosAppId: string;
  tencentCosSecretId: string;
  tencentCosSecretKey: string;
  tencentCosSecurityToken: string;
  supabaseStorageUrl: string;
  supabaseStorageServiceKey: string;
}

function hostFromUrl(value: string) {
  try {
    return new URL(value).hostname.toLowerCase();
  } catch {
    return '';
  }
}

function isLocalHost(hostname: string) {
  return ['localhost', '127.0.0.1', '::1', '0.0.0.0'].includes(hostname);
}

function isUnsafeSecret(value: string) {
  const normalized = value.trim().toLowerCase();
  return (
    !normalized ||
    normalized.length < 32 ||
    normalized.includes('replace_with') ||
    normalized.includes('change-me') ||
    normalized.includes('changeme') ||
    normalized.includes('your_') ||
    normalized.includes('example')
  );
}

function validateProductionConfig(nextConfig: WorkerConfig) {
  if (!nextConfig.isProduction) return;

  const failures: string[] = [];
  if (nextConfig.storageDefaultProvider === 'local_dev') {
    failures.push('STORAGE_DEFAULT_PROVIDER=local_dev is not allowed in production workers');
  }
  if (!nextConfig.storageDefaultBucket.trim()) {
    failures.push('STORAGE_DEFAULT_BUCKET is required in production workers');
  }
  if (!nextConfig.storageRequireTenantPrefix) {
    failures.push('STORAGE_REQUIRE_TENANT_PREFIX=false is not allowed in production workers');
  }
  if (nextConfig.assetSecurityScanFailOpen) {
    failures.push('WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=true is not allowed in production workers');
  }
  if (nextConfig.platformAuditNotificationAllowInsecureLocalhost) {
    failures.push('WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=true is not allowed in production workers');
  }
  if (nextConfig.platformDunningNotificationAllowInsecureLocalhost) {
    failures.push('WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST=true is not allowed in production workers');
  }
  const scannerModes = nextConfig.assetSecurityScanner
    .split(',')
    .map(item => item.trim().toLowerCase())
    .filter(Boolean);
  const unsupportedScannerModes = scannerModes.filter(mode => mode !== 'metadata_rules' && mode !== 'http');
  if (scannerModes.length === 0 || unsupportedScannerModes.length > 0) {
    failures.push('WORKER_ASSET_SECURITY_SCANNER must include supported modes: metadata_rules,http');
  }
  if (!scannerModes.includes('http')) {
    failures.push('WORKER_ASSET_SECURITY_SCANNER must include http in production workers');
  }
  if (scannerModes.includes('http')) {
    const scannerHost = hostFromUrl(nextConfig.assetSecurityScanHttpEndpoint);
    if (!nextConfig.assetSecurityScanHttpEndpoint.startsWith('https://') || isLocalHost(scannerHost)) {
      failures.push('WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT must be a production HTTPS URL');
    }
    if (isUnsafeSecret(nextConfig.assetSecurityScanHttpToken)) {
      failures.push('WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN must be a strong production secret');
    }
  }
  if (nextConfig.storageDefaultProvider === 'aliyun_oss') {
    if (!nextConfig.aliyunOssAccessKeyId || !nextConfig.aliyunOssAccessKeySecret) {
      failures.push('ALIYUN_OSS_ACCESS_KEY_ID and ALIYUN_OSS_ACCESS_KEY_SECRET are required for aliyun_oss');
    }
    if (!nextConfig.aliyunOssRegion && !nextConfig.aliyunOssEndpoint) {
      failures.push('ALIYUN_OSS_REGION or ALIYUN_OSS_ENDPOINT is required for aliyun_oss');
    }
  }
  if (nextConfig.storageDefaultProvider === 'tencent_cos') {
    if (!nextConfig.tencentCosRegion || !nextConfig.tencentCosAppId || !nextConfig.tencentCosSecretId || !nextConfig.tencentCosSecretKey) {
      failures.push('TENCENT_COS_REGION, TENCENT_COS_APP_ID, TENCENT_COS_SECRET_ID and TENCENT_COS_SECRET_KEY are required for tencent_cos');
    }
  }
  if (nextConfig.storageDefaultProvider === 'supabase_storage') {
    if (!nextConfig.supabaseStorageUrl || !nextConfig.supabaseStorageServiceKey) {
      failures.push('SUPABASE_STORAGE_URL and SUPABASE_STORAGE_SERVICE_KEY are required for supabase_storage');
    }
  }

  if (failures.length > 0) {
    throw new Error(`Invalid production worker configuration: ${failures.join('; ')}`);
  }
}

const nodeEnv = envString('NODE_ENV', 'development');

const loadedConfig: WorkerConfig = {
  nodeEnv,
  isProduction: nodeEnv === 'production',
  databaseUrl: envString('DATABASE_URL', DEFAULT_DATABASE_URL),
  crmBatchSize: envNumber('WORKER_CRM_BATCH_SIZE', 20),
  crmPollIntervalMs: envNumber('WORKER_CRM_POLL_INTERVAL_MS', 10_000),
  crmMaxAttempts: envNumber('WORKER_CRM_MAX_ATTEMPTS', 5),
  crmBackoffSeconds: envList('WORKER_CRM_BACKOFF_SECONDS', '5,30,120,600,1800')
    .map((value: string) => Number(value))
    .filter((value: number) => Number.isFinite(value) && value > 0),
  crmRequestTimeoutMs: envNumber('WORKER_CRM_REQUEST_TIMEOUT_MS', 10_000),
  crmAllowInsecureLocalhost: envBoolean('WORKER_CRM_ALLOW_INSECURE_LOCALHOST', false),
  commerceBatchSize: envNumber('WORKER_COMMERCE_BATCH_SIZE', 20),
  commerceMinAgeSeconds: envNumber('WORKER_COMMERCE_MIN_AGE_SECONDS', 300),
  commerceRequestTimeoutMs: envNumber('WORKER_COMMERCE_REQUEST_TIMEOUT_MS', 10_000),
  providerBillBatchSize: envNumber('WORKER_PROVIDER_BILL_BATCH_SIZE', 5),
  providerBillWorkerId: envString('WORKER_PROVIDER_BILL_ID', `provider-bills-${process.pid}`),
  providerBillClaimStaleSeconds: envNumber('WORKER_PROVIDER_BILL_CLAIM_STALE_SECONDS', 15 * 60),
  platformBillingBatchSize: envNumber('WORKER_PLATFORM_BILLING_BATCH_SIZE', 50),
  platformBillingDaysAhead: envNumber('WORKER_PLATFORM_BILLING_DAYS_AHEAD', 45),
  platformBillingDueDays: envNumber('WORKER_PLATFORM_BILLING_DUE_DAYS', 15),
  platformBillingWorkerId: envString('WORKER_PLATFORM_BILLING_ID', `platform-billing-${process.pid}`),
  platformUsageBatchSize: envNumber('WORKER_PLATFORM_USAGE_BATCH_SIZE', 100),
  platformUsageWorkerId: envString('WORKER_PLATFORM_USAGE_ID', `platform-usage-${process.pid}`),
  platformUsageMonth: envString('WORKER_PLATFORM_USAGE_MONTH', ''),
  platformUsageOverageBatchSize: envNumber('WORKER_PLATFORM_USAGE_OVERAGE_BATCH_SIZE', 100),
  platformUsageOverageWorkerId: envString('WORKER_PLATFORM_USAGE_OVERAGE_ID', `platform-usage-overage-${process.pid}`),
  platformUsageOverageMonth: envString('WORKER_PLATFORM_USAGE_OVERAGE_MONTH', ''),
  platformUsageOverageDueDays: envNumber('WORKER_PLATFORM_USAGE_OVERAGE_DUE_DAYS', 15),
  platformDunningBatchSize: envNumber('WORKER_PLATFORM_DUNNING_BATCH_SIZE', 100),
  platformDunningWorkerId: envString('WORKER_PLATFORM_DUNNING_ID', `platform-dunning-${process.pid}`),
  platformDunningNotificationBatchSize: envNumber('WORKER_PLATFORM_DUNNING_NOTIFICATION_BATCH_SIZE', 50),
  platformDunningNotificationMaxAttempts: envNumber('WORKER_PLATFORM_DUNNING_NOTIFICATION_MAX_ATTEMPTS', 5),
  platformDunningNotificationBackoffSeconds: envList('WORKER_PLATFORM_DUNNING_NOTIFICATION_BACKOFF_SECONDS', '10,60,300,900,1800')
    .map((value: string) => Number(value))
    .filter((value: number) => Number.isFinite(value) && value > 0),
  platformDunningNotificationRequestTimeoutMs: envNumber('WORKER_PLATFORM_DUNNING_NOTIFICATION_REQUEST_TIMEOUT_MS', 10_000),
  platformDunningNotificationAllowInsecureLocalhost: envBoolean('WORKER_PLATFORM_DUNNING_NOTIFICATION_ALLOW_INSECURE_LOCALHOST', false),
  platformAuditAlertBatchSize: envNumber('WORKER_PLATFORM_AUDIT_ALERT_BATCH_SIZE', 200),
  platformAuditAlertWorkerId: envString('WORKER_PLATFORM_AUDIT_ALERT_ID', `platform-audit-alerts-${process.pid}`),
  platformAuditAlertLookbackDays: envNumber('WORKER_PLATFORM_AUDIT_ALERT_LOOKBACK_DAYS', 14),
  platformAuditNotificationBatchSize: envNumber('WORKER_PLATFORM_AUDIT_NOTIFICATION_BATCH_SIZE', 50),
  platformAuditNotificationMaxAttempts: envNumber('WORKER_PLATFORM_AUDIT_NOTIFICATION_MAX_ATTEMPTS', 5),
  platformAuditNotificationBackoffSeconds: envList('WORKER_PLATFORM_AUDIT_NOTIFICATION_BACKOFF_SECONDS', '10,60,300,900,1800')
    .map((value: string) => Number(value))
    .filter((value: number) => Number.isFinite(value) && value > 0),
  platformAuditNotificationRequestTimeoutMs: envNumber('WORKER_PLATFORM_AUDIT_NOTIFICATION_REQUEST_TIMEOUT_MS', 10_000),
  platformAuditNotificationAllowInsecureLocalhost: envBoolean('WORKER_PLATFORM_AUDIT_NOTIFICATION_ALLOW_INSECURE_LOCALHOST', false),
  assetBatchSize: envNumber('WORKER_ASSET_BATCH_SIZE', 50),
  assetMinAgeSeconds: envNumber('WORKER_ASSET_MIN_AGE_SECONDS', 300),
  assetRecheckIntervalSeconds: envNumber('WORKER_ASSET_RECHECK_INTERVAL_SECONDS', 60 * 60 * 24),
  assetRequestTimeoutMs: envNumber('WORKER_ASSET_REQUEST_TIMEOUT_MS', 10_000),
  assetSecurityScanner: envString('WORKER_ASSET_SECURITY_SCANNER', 'metadata_rules'),
  assetSecurityScanHttpEndpoint: envString('WORKER_ASSET_SECURITY_SCAN_HTTP_ENDPOINT', ''),
  assetSecurityScanHttpToken: envString('WORKER_ASSET_SECURITY_SCAN_HTTP_TOKEN', ''),
  assetSecurityScanHttpTimeoutMs: envNumber('WORKER_ASSET_SECURITY_SCAN_HTTP_TIMEOUT_MS', 10_000),
  assetSecurityScanFailOpen: envBoolean('WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN', false),
  importBatchSize: envNumber('WORKER_IMPORT_BATCH_SIZE', 5),
  importWorkerId: envString('WORKER_IMPORT_ID', `imports-${process.pid}`),
  importBackoffSeconds: envList('WORKER_IMPORT_BACKOFF_SECONDS', '30,120,600,1800')
    .map((value: string) => Number(value))
    .filter((value: number) => Number.isFinite(value) && value > 0),
  publicBankSyncBatchSize: envNumber('WORKER_PUBLIC_BANK_SYNC_BATCH_SIZE', 5),
  publicBankSyncCopyLimit: envNumber('WORKER_PUBLIC_BANK_SYNC_COPY_LIMIT', 1000),
  publicBankSyncWorkerId: envString('WORKER_PUBLIC_BANK_SYNC_ID', `public-banks-${process.pid}`),
  publicBankSyncClaimStaleSeconds: envNumber('WORKER_PUBLIC_BANK_SYNC_CLAIM_STALE_SECONDS', 15 * 60),
  exportBatchSize: envNumber('WORKER_EXPORT_BATCH_SIZE', 5),
  exportWorkerId: envString('WORKER_EXPORT_ID', `exports-${process.pid}`),
  exportBackoffSeconds: envList('WORKER_EXPORT_BACKOFF_SECONDS', '30,120,600,1800')
    .map((value: string) => Number(value))
    .filter((value: number) => Number.isFinite(value) && value > 0),
  studentSupervisionBatchSize: envNumber('WORKER_STUDENT_SUPERVISION_BATCH_SIZE', 20),
  studentSupervisionWorkerId: envString('WORKER_STUDENT_SUPERVISION_ID', `student-supervision-${process.pid}`),
  studentSupervisionClaimStaleSeconds: envNumber('WORKER_STUDENT_SUPERVISION_CLAIM_STALE_SECONDS', 15 * 60),
  exportLocalStorageRoot: envString('EXPORT_LOCAL_STORAGE_ROOT', '.local-storage'),
  exportPdfFontPath: envString('EXPORT_PDF_FONT_PATH', ''),
  storageDefaultProvider: envString('STORAGE_DEFAULT_PROVIDER', 'local_dev'),
  storageDefaultBucket: envString('STORAGE_DEFAULT_BUCKET', 'tenant-assets'),
  storagePublicBaseUrl: envString('STORAGE_PUBLIC_BASE_URL', ''),
  storageMaxUploadBytes: envNumber('STORAGE_MAX_UPLOAD_BYTES', 1024 * 1024 * 500),
  storageAllowedMimePrefixes: envList('STORAGE_ALLOWED_MIME_PREFIXES', 'image/,video/,audio/'),
  storageAllowedMimeTypes: envList(
    'STORAGE_ALLOWED_MIME_TYPES',
    [
      'application/pdf',
      'application/json',
      'application/zip',
      'application/x-zip-compressed',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'application/vnd.ms-powerpoint',
      'application/vnd.openxmlformats-officedocument.presentationml.presentation',
      'application/octet-stream',
      'text/plain',
      'text/markdown',
      'text/csv',
    ].join(','),
  ),
  storageRequireTenantPrefix: envBoolean('STORAGE_REQUIRE_TENANT_PREFIX', true),
  aliyunOssRegion: envString('ALIYUN_OSS_REGION', ''),
  aliyunOssEndpoint: envString('ALIYUN_OSS_ENDPOINT', ''),
  aliyunOssAccessKeyId: envString('ALIYUN_OSS_ACCESS_KEY_ID', ''),
  aliyunOssAccessKeySecret: envString('ALIYUN_OSS_ACCESS_KEY_SECRET', ''),
  aliyunOssStsToken: envString('ALIYUN_OSS_STS_TOKEN', ''),
  aliyunOssInternal: envBoolean('ALIYUN_OSS_INTERNAL', false),
  tencentCosRegion: envString('TENCENT_COS_REGION', ''),
  tencentCosAppId: envString('TENCENT_COS_APP_ID', ''),
  tencentCosSecretId: envString('TENCENT_COS_SECRET_ID', ''),
  tencentCosSecretKey: envString('TENCENT_COS_SECRET_KEY', ''),
  tencentCosSecurityToken: envString('TENCENT_COS_SECURITY_TOKEN', ''),
  supabaseStorageUrl: envString('SUPABASE_STORAGE_URL', ''),
  supabaseStorageServiceKey: envString('SUPABASE_STORAGE_SERVICE_KEY', ''),
};

validateProductionConfig(loadedConfig);

export const config = loadedConfig;
