/**
 * ═══════════════════════════════════════════════════════════
 * 🧠 铸渊运维守卫 · 永久记忆系统
 * ═══════════════════════════════════════════════════════════
 *
 * 编号: ZY-OPS-MEM-001
 * 签发: 铸渊 · ICE-GL-ZY001
 * 版权: 国作登字-2026-A-00037559
 *
 * 存储层级:
 *   1. 文件存储（始终可用，JSON文件）
 *   2. PostgreSQL（可选，连接到 age_os 数据库时启用）
 *
 * 记忆类型:
 *   - event: 巡检事件（健康检查、修复、告警）
 *   - repair: 修复记录
 *   - ticket: 工单记录
 *   - chat: 对话历史
 *   - config: 配置变更
 */

'use strict';

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

// ── 文件存储路径 ──────────────────────────

const MEMORY_DIR = process.env.OPS_MEMORY_DIR ||
  path.join(__dirname, 'data');
const EVENTS_FILE = path.join(MEMORY_DIR, 'events.jsonl');
const TICKETS_FILE = path.join(MEMORY_DIR, 'tickets.json');
const STATS_FILE = path.join(MEMORY_DIR, 'stats.json');

// 确保目录存在
function ensureDir() {
  try {
    if (!fs.existsSync(MEMORY_DIR)) {
      fs.mkdirSync(MEMORY_DIR, { recursive: true });
    }
  } catch { /* ignore if already exists */ }
}

// ── 事件日志（追加写入 JSONL） ─────────────

function logEvent(type, data) {
  ensureDir();
  const entry = {
    id: `EVT-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
    type,
    timestamp: new Date().toISOString(),
    ...data
  };
  try {
    fs.appendFileSync(EVENTS_FILE, JSON.stringify(entry) + '\n');
  } catch (err) {
    console.error(`[Memory] 写入事件失败: ${err.message}`);
  }
  return entry;
}

/**
 * 查询最近的事件
 * @param {Object} filter - { type, since, limit }
 */
function queryEvents(filter = {}) {
  const { type, since, limit = 50 } = filter;
  try {
    if (!fs.existsSync(EVENTS_FILE)) return [];
    const lines = fs.readFileSync(EVENTS_FILE, 'utf-8')
      .split('\n')
      .filter(l => l.trim());

    let events = [];
    // 从末尾往前读，提高效率
    for (let i = lines.length - 1; i >= 0 && events.length < limit * 2; i--) {
      try {
        events.push(JSON.parse(lines[i]));
      } catch { /* skip malformed lines */ }
    }

    if (type) {
      events = events.filter(e => e.type === type);
    }
    if (since) {
      const sinceTime = new Date(since).getTime();
      events = events.filter(e => new Date(e.timestamp).getTime() >= sinceTime);
    }

    return events.slice(0, limit);
  } catch {
    return [];
  }
}

// ── 工单管理 ──────────────────────────────

function loadTickets() {
  try {
    if (!fs.existsSync(TICKETS_FILE)) return [];
    return JSON.parse(fs.readFileSync(TICKETS_FILE, 'utf-8'));
  } catch {
    return [];
  }
}

function saveTickets(tickets) {
  ensureDir();
  fs.writeFileSync(TICKETS_FILE, JSON.stringify(tickets, null, 2));
}

function createTicket(data) {
  const tickets = loadTickets();
  const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, '');
  const todayCount = tickets.filter(t => t.id.includes(dateStr)).length;
  const ticket = {
    id: `OPS-${dateStr}-${String(todayCount + 1).padStart(3, '0')}`,
    title: data.title,
    description: data.description,
    severity: data.severity || 'medium',
    category: data.category || 'unknown',
    source: 'ops-agent',
    direction: data.direction || '待判断',
    status: 'open',
    diagnosis: data.diagnosis || '',
    suggestedFix: data.suggestedFix || '',
    relatedService: data.relatedService || '',
    autoRepairAttempts: data.autoRepairAttempts || 0,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    resolvedAt: null
  };
  tickets.push(ticket);
  saveTickets(tickets);

  // 同时记为事件
  logEvent('ticket', { ticketId: ticket.id, title: ticket.title, severity: ticket.severity });

  return ticket;
}

function updateTicket(ticketId, updates) {
  const tickets = loadTickets();
  const idx = tickets.findIndex(t => t.id === ticketId);
  if (idx === -1) return null;
  Object.assign(tickets[idx], updates, { updatedAt: new Date().toISOString() });
  if (updates.status === 'resolved') {
    tickets[idx].resolvedAt = new Date().toISOString();
  }
  saveTickets(tickets);
  return tickets[idx];
}

function getOpenTickets() {
  return loadTickets().filter(t => t.status === 'open');
}

/**
 * 按服务+诊断查找处于 open 状态的工单（去重用）
 * 严格匹配：必须同时提供 relatedService 与 diagnosis，避免跨问题误合并
 * @param {Object} key - { relatedService, diagnosis }
 * @returns {Object|null}
 */
function findOpenTicketByKey(key = {}) {
  const { relatedService, diagnosis } = key;
  if (!relatedService || !diagnosis) return null;
  const tickets = loadTickets();
  return tickets.find(t =>
    t.status === 'open' &&
    t.relatedService === relatedService &&
    t.diagnosis === diagnosis
  ) || null;
}

function getAllTickets(limit = 100) {
  return loadTickets().slice(-limit);
}

// ── 统计数据 ──────────────────────────────

function loadStats() {
  try {
    if (!fs.existsSync(STATS_FILE)) return getDefaultStats();
    return JSON.parse(fs.readFileSync(STATS_FILE, 'utf-8'));
  } catch {
    return getDefaultStats();
  }
}

function saveStats(stats) {
  ensureDir();
  fs.writeFileSync(STATS_FILE, JSON.stringify(stats, null, 2));
}

function getDefaultStats() {
  return {
    totalChecks: 0,
    totalRepairs: 0,
    totalTickets: 0,
    totalChats: 0,
    lastQuickCheck: null,
    lastDeepCheck: null,
    lastDailyReport: null,
    uptimeStart: new Date().toISOString(),
    issuesByCategory: {},
    repairSuccessRate: { success: 0, fail: 0 }
  };
}

function incrementStat(key, amount = 1) {
  const stats = loadStats();
  if (typeof stats[key] === 'number') {
    stats[key] += amount;
  } else {
    stats[key] = amount;
  }
  saveStats(stats);
  return stats;
}

function updateStatField(key, value) {
  const stats = loadStats();
  stats[key] = value;
  saveStats(stats);
  return stats;
}

// ── 上下文构建（给 LLM 提供历史记忆） ────────

function buildMemoryContext() {
  const stats = loadStats();
  const recentEvents = queryEvents({ limit: 10 });
  const openTickets = getOpenTickets();

  let context = '## 运维守卫记忆\n';
  context += `- 启动时间: ${stats.uptimeStart}\n`;
  context += `- 总巡检次数: ${stats.totalChecks}\n`;
  context += `- 总修复次数: ${stats.totalRepairs}\n`;
  context += `- 开放工单数: ${openTickets.length}\n`;

  if (recentEvents.length > 0) {
    context += '\n## 最近事件\n';
    for (const evt of recentEvents.slice(0, 5)) {
      context += `- [${evt.timestamp}] ${evt.type}: ${evt.summary || evt.title || JSON.stringify(evt).slice(0, 100)}\n`;
    }
  }

  if (openTickets.length > 0) {
    context += '\n## 开放工单\n';
    for (const t of openTickets) {
      context += `- ${t.id}: ${t.title} (${t.severity}) — ${t.direction}\n`;
    }
  }

  return context;
}

module.exports = {
  logEvent,
  queryEvents,
  createTicket,
  updateTicket,
  getOpenTickets,
  findOpenTicketByKey,
  getAllTickets,
  loadStats,
  incrementStat,
  updateStatField,
  buildMemoryContext,
  MEMORY_DIR
};
