'use strict';

const fs = require('node:fs');
const path = require('node:path');

const CLOUD_STORAGE_PATTERNS = Object.freeze([
  { id: 'dropbox', label: 'Dropbox', regex: /(^|[\\/])(?:Dropbox|CloudStorage[\\/][^\\/]*Dropbox[^\\/]*)([\\/]|$)/i },
  { id: 'icloud', label: 'iCloud Drive', regex: /(^|[\\/])(?:Mobile Documents|CloudStorage[\\/][^\\/]*iCloud[^\\/]*)([\\/]|$)/i },
  { id: 'onedrive', label: 'OneDrive', regex: /(^|[\\/])(?:OneDrive|CloudStorage[\\/][^\\/]*OneDrive[^\\/]*)([\\/]|$)/i },
  { id: 'google-drive', label: 'Google Drive', regex: /(^|[\\/])(?:Google Drive|CloudStorage[\\/][^\\/]*GoogleDrive[^\\/]*)([\\/]|$)/i },
  { id: 'box', label: 'Box Drive', regex: /(^|[\\/])(?:Box|CloudStorage[\\/][^\\/]*Box[^\\/]*)([\\/]|$)/i },
]);

const VALID_POLICY_MODES = new Set(['off', 'warn', 'error']);
const warnedKeys = new Set();

function _normalizePath(value) {
  const raw = String(value || '').trim();
  if (!raw) return '';
  try {
    const dir = path.dirname(raw);
    const base = path.basename(raw);
    const realDir = fs.existsSync(dir) ? fs.realpathSync(dir) : path.resolve(dir);
    return path.join(realDir, base);
  } catch {
    return path.resolve(raw);
  }
}

function _truthy(value) {
  return /^(1|true|yes|on)$/i.test(String(value || '').trim());
}

function sqliteStoragePolicyMode({ env = process.env, prefix = '' } = {}) {
  const normalizedPrefix = String(prefix || '').trim().replace(/[^A-Z0-9_]/gi, '_').toUpperCase();
  if (normalizedPrefix && _truthy(env[`${normalizedPrefix}_ALLOW_UNSAFE_CLOUD_SQLITE`])) return 'off';
  if (_truthy(env.ALLOW_UNSAFE_CLOUD_SQLITE)) return 'off';

  const raw = String(
    (normalizedPrefix && env[`${normalizedPrefix}_SQLITE_STORAGE_POLICY`])
    || env.SQLITE_STORAGE_POLICY
    || 'warn'
  ).trim().toLowerCase();
  return VALID_POLICY_MODES.has(raw) ? raw : 'warn';
}

function classifySqlitePathRisk(dbPath) {
  const normalizedPath = _normalizePath(dbPath);
  const normalizedForMatch = normalizedPath.replace(/\\/g, '/');
  const match = CLOUD_STORAGE_PATTERNS.find((candidate) => candidate.regex.test(normalizedForMatch));
  if (!match) {
    return {
      risky: false,
      reason: '',
      provider: '',
      providerLabel: '',
      dbPath: normalizedPath,
      message: '',
    };
  }
  return {
    risky: true,
    reason: 'cloud_sync_sqlite_wal',
    provider: match.id,
    providerLabel: match.label,
    dbPath: normalizedPath,
    message:
      `${match.label} sync storage is unsafe for a live SQLite WAL database. `
      + 'Move the live DB directory to a local disk path and sync backups/exports instead.',
  };
}

function enforceSqliteStoragePolicy(dbPath, {
  env = process.env,
  prefix = '',
  label = 'SQLite database',
  logger = console,
} = {}) {
  const risk = classifySqlitePathRisk(dbPath);
  const mode = sqliteStoragePolicyMode({ env, prefix });
  const result = { ...risk, mode, blocked: false, label };
  if (!risk.risky || mode === 'off') return result;

  const message = `${label}: ${risk.message} Path: ${risk.dbPath}`;
  if (mode === 'error') {
    const err = new Error(message);
    err.code = 'SQLITE_UNSAFE_CLOUD_STORAGE';
    err.storageRisk = result;
    throw err;
  }

  const warnKey = `${label}:${risk.provider}:${risk.dbPath}`;
  if (!warnedKeys.has(warnKey)) {
    warnedKeys.add(warnKey);
    try {
      logger.warn(`[sqlite-storage] ${message}`);
    } catch {}
  }
  return result;
}

function resetSqliteStoragePolicyWarnings() {
  warnedKeys.clear();
}

module.exports = {
  classifySqlitePathRisk,
  enforceSqliteStoragePolicy,
  resetSqliteStoragePolicyWarnings,
  sqliteStoragePolicyMode,
};
