'use strict';

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

const CACHE_VERSION = 1;
const CACHE_FILENAME = 'model-overview-cache.json';

function cacheFilePath(configDir) {
  return path.join(configDir, CACHE_FILENAME);
}

function asArray(value) {
  return Array.isArray(value) ? value : [];
}

function asObject(value) {
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
}

const SAFE_SECRET_NAMED_FIELDS = new Set([
  'authorization_status',
  'credential_count',
  'credential_set',
  'credential_source',
  'credential_status',
  'has_api_key',
  'has_key',
  'has_runtime_access',
  'has_stored_key',
]);

function normalizedFieldName(key) {
  return String(key || '')
    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    .replace(/[^a-z0-9]+/gi, '_')
    .replace(/^_+|_+$/g, '')
    .toLowerCase();
}

function isSensitiveFieldName(key) {
  const normalized = normalizedFieldName(key);
  if (!normalized || SAFE_SECRET_NAMED_FIELDS.has(normalized)) return false;
  return /(?:^|_)(?:api_?key|apikey|secret|token|password|authorization|cookie|custom_headers|auth_token|refresh_token|access_token|client_secret|private_key)(?:$|_)/i.test(normalized);
}

function sanitizeOverviewValue(value, depth = 0) {
  if (depth > 8) return null;
  if (Array.isArray(value)) return value.map((item) => sanitizeOverviewValue(item, depth + 1));
  if (!value || typeof value !== 'object') return value;
  const out = {};
  for (const [key, entry] of Object.entries(value)) {
    if (isSensitiveFieldName(key)) continue;
    const sanitized = sanitizeOverviewValue(entry, depth + 1);
    if (sanitized !== undefined) out[key] = sanitized;
  }
  return out;
}

function sanitizeOverviewArray(value) {
  return asArray(value).map((item) => sanitizeOverviewValue(item));
}

function sanitizeOverviewObject(value) {
  return sanitizeOverviewValue(asObject(value));
}

function sanitizeWarning(warning) {
  if (typeof warning === 'string') return { message: warning };
  if (!warning || typeof warning !== 'object') return null;
  const out = {};
  ['code', 'where', 'provider_id', 'provider_type', 'message', 'detail'].forEach((key) => {
    if (warning[key] !== undefined && warning[key] !== null) out[key] = String(warning[key]);
  });
  return Object.keys(out).length ? out : null;
}

function sanitizeWarnings(warnings) {
  return asArray(warnings).map(sanitizeWarning).filter(Boolean).slice(0, 20);
}

function sanitizeOverviewPayload(payload = {}) {
  const sourcePayload = asObject(payload);
  return {
    providers: sanitizeOverviewArray(sourcePayload.providers),
    models: sanitizeOverviewArray(sourcePayload.models),
    gateways: sanitizeOverviewArray(sourcePayload.gateways),
    health: sanitizeOverviewObject(sourcePayload.health),
    tier_defaults: sanitizeOverviewObject(sourcePayload.tier_defaults || sourcePayload.tierDefaults),
    defaults: sanitizeOverviewObject(sourcePayload.defaults),
    shadow_config: sanitizeOverviewObject(sourcePayload.shadow_config || sourcePayload.shadowConfig),
    catalog_status: sourcePayload.catalog_status || 'ok',
    catalog_warnings: sanitizeWarnings(sourcePayload.catalog_warnings),
    optional_failures: sanitizeWarnings(sourcePayload.optional_failures),
  };
}

function readModelOverviewCache(configDir, nowMs = Date.now()) {
  try {
    const raw = fs.readFileSync(cacheFilePath(configDir), 'utf8');
    const parsed = JSON.parse(raw);
    if (!parsed || parsed.version !== CACHE_VERSION || !parsed.payload) return null;
    const cachedAtMs = Date.parse(String(parsed.cached_at || ''));
    const ageMs = Number.isFinite(cachedAtMs) ? Math.max(0, nowMs - cachedAtMs) : null;
    return {
      ...sanitizeOverviewPayload(parsed.payload),
      source: 'cache',
      stale: true,
      cache: {
        version: parsed.version,
        cached_at: parsed.cached_at || null,
        age_ms: ageMs,
      },
    };
  } catch {
    return null;
  }
}

function writeModelOverviewCache(configDir, payload, nowMs = Date.now()) {
  const file = cacheFilePath(configDir);
  fs.mkdirSync(configDir, { recursive: true });
  const record = {
    version: CACHE_VERSION,
    cached_at: new Date(nowMs).toISOString(),
    payload: sanitizeOverviewPayload({ ...payload, models: [] }),
  };
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
  fs.writeFileSync(tmp, JSON.stringify(record, null, 2));
  fs.renameSync(tmp, file);
  return readModelOverviewCache(configDir, nowMs);
}

module.exports = {
  CACHE_FILENAME,
  CACHE_VERSION,
  cacheFilePath,
  readModelOverviewCache,
  sanitizeOverviewPayload,
  writeModelOverviewCache,
};
