/**
 * Analytics Engine — Metrics collection and JSONL storage
 *
 * Records events to append-only JSONL files:
 * - .morph/analytics/threads-log.jsonl
 * - .morph/analytics/context-log.jsonl
 * - .morph/analytics/trust-log.jsonl
 *
 * Provides aggregation, ASCII chart generation, and 30-day dashboards.
 * Auto-prunes entries older than 90 days.
 */

import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';

const ANALYTICS_DIR = join(process.cwd(), '.morph/analytics');
const THREADS_LOG = join(ANALYTICS_DIR, 'threads-log.jsonl');
const CONTEXT_LOG = join(ANALYTICS_DIR, 'context-log.jsonl');
const TRUST_LOG = join(ANALYTICS_DIR, 'trust-log.jsonl');
const PRUNE_DAYS = 90;
const DASHBOARD_DAYS = 30;

// ============================================================================
// Internal Helpers
// ============================================================================

function ensureAnalyticsDir() {
  if (!existsSync(ANALYTICS_DIR)) {
    mkdirSync(ANALYTICS_DIR, { recursive: true });
  }
}

function appendJSONL(filePath, record) {
  ensureAnalyticsDir();
  appendFileSync(filePath, JSON.stringify(record) + '\n', 'utf8');
}

function readJSONL(filePath) {
  if (!existsSync(filePath)) return [];

  const lines = readFileSync(filePath, 'utf8').trim().split('\n').filter(Boolean);
  const records = [];

  for (const line of lines) {
    try {
      records.push(JSON.parse(line));
    } catch {
      // Skip malformed lines
    }
  }
  return records;
}

function pruneJSONL(filePath) {
  if (!existsSync(filePath)) return;

  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - PRUNE_DAYS);

  const records = readJSONL(filePath);
  const fresh = records.filter(r => {
    const ts = new Date(r.timestamp || r.createdAt || 0);
    return ts > cutoff;
  });

  if (fresh.length < records.length) {
    writeFileSync(filePath, fresh.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf8');
  }
}

function recentRecords(records, days = DASHBOARD_DAYS) {
  const cutoff = new Date();
  cutoff.setDate(cutoff.getDate() - days);
  return records.filter(r => {
    const ts = new Date(r.timestamp || r.createdAt || 0);
    return ts > cutoff;
  });
}

// ============================================================================
// Event Recording
// ============================================================================

/**
 * Record a thread analytics event
 * @param {Object} event
 * @param {string} event.type - Event type (thread_created|thread_completed|thread_failed|checkpoint_passed|etc.)
 * @param {string} event.feature - Feature name
 * @param {string} [event.threadId] - Thread ID
 * @param {string} [event.agent] - Agent name
 * @param {Object} [event.data] - Additional event data
 */
export function recordEvent(event) {
  const record = {
    ...event,
    timestamp: new Date().toISOString()
  };

  // Route to appropriate log
  if (event.type?.startsWith('context_') || event.type === 'token_usage') {
    appendJSONL(CONTEXT_LOG, record);
  } else if (event.type?.startsWith('trust_')) {
    appendJSONL(TRUST_LOG, record);
  } else {
    appendJSONL(THREADS_LOG, record);
  }
}

/**
 * Record a context event (token usage, optimization, etc.)
 * @param {Object} event
 */
export function recordContextEvent(event) {
  recordEvent({ ...event, type: event.type || 'context_usage' });
}

/**
 * Record a trust level change event
 * @param {string} feature
 * @param {string} level - new trust level
 * @param {string} reason
 */
export function recordTrustEvent(feature, level, reason = '') {
  appendJSONL(TRUST_LOG, {
    timestamp: new Date().toISOString(),
    type: 'trust_level_changed',
    feature,
    level,
    reason
  });
}

/**
 * Record auto-approval event (gate approved by trust)
 * @param {string} feature
 * @param {string} gate - 'design' | 'tasks' | 'proposal'
 * @param {string} trustLevel
 * @param {number} passRate
 */
export function recordAutoApproval(feature, gate, trustLevel, passRate) {
  appendJSONL(TRUST_LOG, {
    timestamp: new Date().toISOString(),
    type: 'trust_auto_approved',
    feature,
    gate,
    trustLevel,
    passRate,
    timeSavedMinutes: 5 // estimated minutes saved per auto-approval
  });
}

/**
 * Get trust progression for a feature
 * @param {string} featureName
 * @returns {Array} Trust level history
 */
export function getTrustProgression(featureName) {
  const events = readJSONL(TRUST_LOG)
    .filter(e => e.feature === featureName && e.type === 'trust_level_changed')
    .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
  return events;
}

// ============================================================================
// Feature Analytics
// ============================================================================

/**
 * Get analytics for a specific feature
 * @param {string} feature - Feature name
 * @returns {Object} Feature analytics
 */
export function getFeatureAnalytics(feature) {
  const allThreadEvents = readJSONL(THREADS_LOG).filter(r => r.feature === feature);
  const allContextEvents = readJSONL(CONTEXT_LOG).filter(r => r.feature === feature);

  const threadsByStatus = allThreadEvents
    .filter(e => e.type === 'thread_completed' || e.type === 'thread_failed')
    .reduce((acc, e) => {
      const status = e.type === 'thread_completed' ? 'completed' : 'failed';
      acc[status] = (acc[status] || 0) + 1;
      return acc;
    }, {});

  const checkpoints = allThreadEvents.filter(e =>
    e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
  );
  const checkpointPassRate = checkpoints.length > 0
    ? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
    : 100;

  const totalDuration = allThreadEvents
    .filter(e => e.type === 'thread_completed' && e.data?.duration)
    .reduce((sum, e) => sum + e.data.duration, 0);

  const toolCallEvents = allThreadEvents.filter(e => e.type === 'tool_call');
  const tokenEvents = allContextEvents.filter(e => e.data?.tokensUsed);
  const totalTokens = tokenEvents.reduce((sum, e) => sum + (e.data.tokensUsed || 0), 0);

  return {
    feature,
    threads: threadsByStatus,
    checkpointPassRate,
    totalDuration,
    totalToolCalls: toolCallEvents.length,
    totalTokensUsed: totalTokens,
    optimizationEvents: allContextEvents.filter(e => e.type === 'context_optimized').length
  };
}

/**
 * Get project-wide analytics dashboard (30-day summary)
 * @returns {Object} Project analytics
 */
export function getProjectAnalytics() {
  const allThreadEvents = recentRecords(readJSONL(THREADS_LOG));
  const allContextEvents = recentRecords(readJSONL(CONTEXT_LOG));
  const allTrustEvents = recentRecords(readJSONL(TRUST_LOG));

  const features = [...new Set(allThreadEvents.map(e => e.feature).filter(Boolean))];

  const completedThreads = allThreadEvents.filter(e => e.type === 'thread_completed');
  const failedThreads = allThreadEvents.filter(e => e.type === 'thread_failed');
  const checkpoints = allThreadEvents.filter(e =>
    e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
  );

  const parallelEvents = allThreadEvents.filter(e => e.data?.type === 'parallel');
  const avgParallel = parallelEvents.length > 0
    ? parallelEvents.reduce((sum, e) => sum + (e.data?.concurrency || 1), 0) / parallelEvents.length
    : 1;

  const autoApprovalEvents = allTrustEvents.filter(e => e.type === 'trust_auto_approved');
  const avgTokens = allContextEvents.filter(e => e.data?.tokensUsed).length > 0
    ? Math.round(allContextEvents.reduce((sum, e) => sum + (e.data?.tokensUsed || 0), 0) / allContextEvents.filter(e => e.data?.tokensUsed).length)
    : 0;

  return {
    period: `${DASHBOARD_DAYS} days`,
    features: features.length,
    threads: {
      total: completedThreads.length + failedThreads.length,
      completed: completedThreads.length,
      failed: failedThreads.length
    },
    checkpoints: {
      total: checkpoints.length,
      passed: checkpoints.filter(e => e.type === 'checkpoint_passed').length,
      passRate: checkpoints.length > 0
        ? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
        : 100
    },
    parallelization: {
      avgConcurrency: Math.round(avgParallel * 10) / 10,
      parallelThreads: parallelEvents.length
    },
    trust: {
      autoApprovals: autoApprovalEvents.length,
      trustChanges: allTrustEvents.filter(e => e.type === 'trust_level_changed').length
    },
    context: {
      avgTokensPerSession: avgTokens,
      optimizations: allContextEvents.filter(e => e.type === 'context_optimized').length
    }
  };
}

// ============================================================================
// ASCII Charts
// ============================================================================

/**
 * Generate an ASCII bar chart
 * @param {Object} data - { label: value }
 * @param {Object} [opts]
 * @param {number} [opts.width=40] - Chart width in chars
 * @param {string} [opts.title] - Chart title
 * @returns {string} ASCII chart string
 */
export function generateAsciiChart(data, { width = 40, title = '' } = {}) {
  const entries = Object.entries(data);
  if (entries.length === 0) return '  (no data)';

  const maxValue = Math.max(...entries.map(([, v]) => v), 1);
  const maxLabelLen = Math.max(...entries.map(([k]) => k.length));
  const lines = [];

  if (title) {
    lines.push(`  ${title}`);
    lines.push('  ' + '─'.repeat(width + maxLabelLen + 5));
  }

  for (const [label, value] of entries) {
    const barLen = Math.round((value / maxValue) * width);
    const bar = '█'.repeat(barLen);
    const paddedLabel = label.padStart(maxLabelLen);
    lines.push(`  ${paddedLabel} │${bar} ${value}`);
  }

  return lines.join('\n');
}

/**
 * Generate thread timeline ASCII chart
 * @param {Array} events - Array of thread events with timestamps
 * @param {string} label - Chart label
 * @returns {string} ASCII timeline
 */
export function generateTimelineChart(events, label = 'Timeline') {
  if (events.length === 0) return '  (no data)';

  const now = new Date();
  const days = Array.from({ length: 7 }, (_, i) => {
    const d = new Date(now);
    d.setDate(d.getDate() - (6 - i));
    return d.toLocaleDateString('en-US', { weekday: 'short' });
  });

  const counts = new Array(7).fill(0);
  for (const event of events) {
    const ts = new Date(event.timestamp);
    const daysAgo = Math.floor((now - ts) / (1000 * 60 * 60 * 24));
    if (daysAgo < 7) {
      counts[6 - daysAgo]++;
    }
  }

  const data = {};
  days.forEach((d, i) => { data[d] = counts[i]; });

  return generateAsciiChart(data, { title: label });
}

// ============================================================================
// Maintenance
// ============================================================================

/**
 * Prune all analytics logs (remove entries older than 90 days)
 */
export function pruneAnalytics() {
  pruneJSONL(THREADS_LOG);
  pruneJSONL(CONTEXT_LOG);
  pruneJSONL(TRUST_LOG);
}
