import fs from "fs";
import path from "path";
import crypto from "crypto";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const LOG_PATH = path.join(__dirname, "..", "data", "access-log.json");

// Enkripsi AES-256-GCM untuk data sensitif log akses
const ENCRYPTION_KEY = (() => {
  // Derived from env or fixed seed (SHA-256 of a secret phrase)
  const seed = process.env.ACCESS_LOG_SECRET || "KLING-TEKNO-ACCESS-LOG-SECRET-v3";
  return crypto.createHash("sha256").update(seed).digest();
})();

function encrypt(text) {
  if (!text) return null;
  try {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv("aes-256-gcm", ENCRYPTION_KEY, iv);
    let encrypted = cipher.update(text, "utf8", "hex");
    encrypted += cipher.final("hex");
    const authTag = cipher.getAuthTag().toString("hex");
    return JSON.stringify({ iv: iv.toString("hex"), data: encrypted, tag: authTag });
  } catch {
    return text; // fallback: store plaintext if encryption fails
  }
}

function decrypt(encryptedJson) {
  if (!encryptedJson) return null;
  try {
    const parsed = typeof encryptedJson === "string" ? JSON.parse(encryptedJson) : encryptedJson;
    if (!parsed.iv || !parsed.data || !parsed.tag) return encryptedJson; // not encrypted
    const iv = Buffer.from(parsed.iv, "hex");
    const decipher = crypto.createDecipheriv("aes-256-gcm", ENCRYPTION_KEY, iv);
    decipher.setAuthTag(Buffer.from(parsed.tag, "hex"));
    let decrypted = decipher.update(parsed.data, "hex", "utf8");
    decrypted += decipher.final("utf8");
    return decrypted;
  } catch {
    return encryptedJson; // return as-is if decryption fails
  }
}

function ensureDir() {
  const dir = path.dirname(LOG_PATH);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function readLogs() {
  ensureDir();
  try {
    if (fs.existsSync(LOG_PATH)) {
      const raw = JSON.parse(fs.readFileSync(LOG_PATH, "utf-8"));
      // Decrypt sensitive fields for internal use
      return (raw.entries || []).map((entry) => ({
        ...entry,
        actorName: decrypt(entry.actorNameEncrypted) || entry.actorName || "Unknown",
        description: decrypt(entry.descriptionEncrypted) || entry.description || "",
      }));
    }
  } catch {}
  return [];
}

function writeLogs(entries) {
  ensureDir();
  // Encrypt sensitive fields before saving
  const safe = entries.map((entry) => ({
    id: entry.id,
    timestamp: entry.timestamp,
    type: entry.type,
    actor: entry.actor,
    actorNameEncrypted: encrypt(entry.actorName || "Unknown"),
    action: entry.action,
    targetResellerId: entry.targetResellerId,
    targetResellerNameEncrypted: encrypt(entry.targetResellerName || ""),
    descriptionEncrypted: encrypt(entry.description || ""),
    result: entry.result,
    ip: encrypt(entry.ip || ""),
  }));
  fs.writeFileSync(LOG_PATH, JSON.stringify({ entries: safe, updatedAt: new Date().toISOString() }, null, 2), "utf-8");
}

/**
 * Tambah entri log aktivitas akses penuh
 * @param {Object} param
 * @param {string} param.type - "request" | "grant" | "revoke" | "deny" | "access"
 * @param {string} param.actor - "owner" | resellerId mitra
 * @param {string} param.actorName - nama pelaku
 * @param {string} param.action - deskripsi aksi singkat
 * @param {string} param.targetResellerId - resellerId target (jika ada)
 * @param {string} param.targetResellerName - nama reseller target
 * @param {string} param.description - deskripsi detail
 * @param {string} param.result - "success" | "failed" | "pending"
 * @param {string} param.ip - IP address pelaku
 */
export function logAccessAction({ type, actor, actorName, action, targetResellerId, targetResellerName, description, result, ip }) {
  const logs = readLogs();
  const entry = {
    id: "al_" + Date.now().toString(36) + Math.random().toString(36).substring(2, 8),
    timestamp: new Date().toISOString(),
    type: type || "access",
    actor: actor || "unknown",
    actorName: actorName || "Unknown",
    action: action || "",
    targetResellerId: targetResellerId || null,
    targetResellerName: targetResellerName || "",
    description: description || "",
    result: result || "success",
    ip: ip || "",
  };
  logs.unshift(entry);

  // Keep max 5000 entries
  if (logs.length > 5000) {
    logs.length = 5000;
  }

  writeLogs(logs);
  return entry;
}

/**
 * Ambil log aktivitas akses dengan filter opsional
 */
export function getAccessLogs(filters = {}) {
  let logs = readLogs();

  if (filters.type) {
    logs = logs.filter((l) => l.type === filters.type);
  }
  if (filters.targetResellerId) {
    logs = logs.filter((l) => l.targetResellerId === filters.targetResellerId);
  }
  if (filters.actor) {
    logs = logs.filter((l) => l.actor === filters.actor);
  }
  if (filters.search) {
    const s = filters.search.toLowerCase();
    logs = logs.filter(
      (l) =>
        (l.description || "").toLowerCase().includes(s) ||
        (l.action || "").toLowerCase().includes(s) ||
        (l.actorName || "").toLowerCase().includes(s) ||
        (l.targetResellerName || "").toLowerCase().includes(s)
    );
  }

  const limit = filters.limit || 200;
  const offset = filters.offset || 0;
  const total = logs.length;
  logs = logs.slice(offset, offset + limit);

  return { entries: logs, total, limit, offset };
}

/**
 * Ambil ringkasan aktivitas akses per reseller
 */
export function getAccessSummary() {
  const logs = readLogs();
  const summary = {
    totalRequests: 0,
    totalGrants: 0,
    totalRevokes: 0,
    totalDenials: 0,
    activeFullAccess: [],
    pendingRequests: [],
  };

  for (const log of logs) {
    switch (log.type) {
      case "request": summary.totalRequests++; break;
      case "grant": summary.totalGrants++; break;
      case "revoke": summary.totalRevokes++; break;
      case "deny": summary.totalDenials++; break;
    }
  }

  return summary;
}

/**
 * Hapus log lama (lebih dari 90 hari)
 */
export function cleanOldLogs(daysThreshold = 90) {
  const logs = readLogs();
  const cutoff = Date.now() - daysThreshold * 24 * 60 * 60 * 1000;
  const filtered = logs.filter((l) => new Date(l.timestamp).getTime() > cutoff);
  if (filtered.length < logs.length) {
    writeLogs(filtered);
    return { cleaned: logs.length - filtered.length, remaining: filtered.length };
  }
  return { cleaned: 0, remaining: filtered.length };
}
