import { promises as fs } from 'node:fs';
import path from 'node:path';

import { WORKSPACE_ROOT } from './shared.mjs';

const LOCAL_METRICS_DIR = '.agents/evals/local-metrics';
const LESSONS_DIR = '.agents/evals/lessons';
const WEEKLY_DIGEST_FILE = `${LESSONS_DIR}/weekly-digest.md`;
const AUTO_LESSONS_FILE = `${LESSONS_DIR}/auto-lessons.md`;
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
const PROMOTION_THRESHOLD = 5;

const METRIC_FILES = [
  { source: 'blocks', file: 'blocks.jsonl' },
  { source: 'corrections', file: 'corrections.jsonl' },
  { source: 'reverts', file: 'reverts.jsonl' },
];

export function createStableDigestWindowEnd(date = new Date()) {
  return new Date(
    Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59, 999),
  );
}

async function readJsonl(filePath) {
  try {
    const content = await fs.readFile(filePath, 'utf8');
    return content
      .split(/\r?\n/)
      .map((line) => line.trim())
      .filter(Boolean)
      .flatMap((line) => {
        try {
          return [JSON.parse(line)];
        } catch {
          return [];
        }
      });
  } catch (error) {
    if (error?.code === 'ENOENT') {
      return [];
    }
    throw error;
  }
}

function getPattern(record, source) {
  if (typeof record.pattern === 'string' && record.pattern.trim()) {
    return record.pattern.trim();
  }
  return source;
}

function getExamplePath(record) {
  if (typeof record.file === 'string' && record.file.trim()) {
    return record.file.trim();
  }
  if (typeof record.file_path === 'string' && record.file_path.trim()) {
    return record.file_path.trim();
  }
  return null;
}

function toTimestamp(record) {
  if (typeof record.timestamp !== 'string') {
    return null;
  }
  const timestamp = Date.parse(record.timestamp);
  return Number.isNaN(timestamp) ? null : timestamp;
}

function ensureGroup(groups, pattern) {
  if (!groups.has(pattern)) {
    groups.set(pattern, {
      pattern,
      frequency: 0,
      sources: new Set(),
      examples: new Set(),
      firstSeen: null,
      lastSeen: null,
    });
  }
  return groups.get(pattern);
}

export async function summarizeLessonSignals({
  workspaceRoot = WORKSPACE_ROOT,
  now = createStableDigestWindowEnd(),
  windowMs = SEVEN_DAYS_MS,
} = {}) {
  const windowEnd = now.getTime();
  const windowStart = windowEnd - windowMs;
  const groups = new Map();

  for (const metricFile of METRIC_FILES) {
    const records = await readJsonl(path.join(workspaceRoot, LOCAL_METRICS_DIR, metricFile.file));
    for (const record of records) {
      const timestamp = toTimestamp(record);
      if (timestamp === null || timestamp < windowStart || timestamp > windowEnd) {
        continue;
      }

      const group = ensureGroup(groups, getPattern(record, metricFile.source));
      group.frequency += 1;
      group.sources.add(metricFile.source);
      const examplePath = getExamplePath(record);
      if (examplePath) {
        group.examples.add(examplePath);
      }
      const isoTimestamp = new Date(timestamp).toISOString();
      group.firstSeen =
        group.firstSeen === null || isoTimestamp < group.firstSeen ? isoTimestamp : group.firstSeen;
      group.lastSeen =
        group.lastSeen === null || isoTimestamp > group.lastSeen ? isoTimestamp : group.lastSeen;
    }
  }

  return Array.from(groups.values())
    .map((group) => ({
      pattern: group.pattern,
      frequency: group.frequency,
      sources: Array.from(group.sources).sort(),
      examples: Array.from(group.examples).sort().slice(0, 5),
      firstSeen: group.firstSeen,
      lastSeen: group.lastSeen,
    }))
    .sort(
      (left, right) =>
        right.frequency - left.frequency || left.pattern.localeCompare(right.pattern),
    );
}

function renderExamples(examples) {
  if (examples.length === 0) {
    return '(none)';
  }
  return examples.map((item) => `\`${item}\``).join(', ');
}

export function renderWeeklyDigest(groups, { now = createStableDigestWindowEnd() } = {}) {
  const windowEnd = now.toISOString();
  const windowStart = new Date(now.getTime() - SEVEN_DAYS_MS).toISOString();
  const lines = [
    '# Weekly Auto-Lessons Digest',
    '',
    `Window: ${windowStart} to ${windowEnd}`,
    '',
    'Generated by `pnpm harness:lessons:digest` from local append-only metrics.',
    '',
    '## Signal Summary',
    '',
  ];

  if (groups.length === 0) {
    lines.push('No auto-lesson signals found for this window.', '');
    return `${lines.join('\n')}\n`;
  }

  lines.push('| Pattern | Events | Sources | First Seen | Last Seen | Examples |');
  lines.push('| ------- | ------ | ------- | ---------- | --------- | -------- |');
  for (const group of groups) {
    lines.push(
      `| \`${group.pattern}\` | ${group.frequency} | ${group.sources.join(', ')} | ${group.firstSeen ?? ''} | ${group.lastSeen ?? ''} | ${renderExamples(group.examples)} |`,
    );
  }
  lines.push('');
  return `${lines.join('\n')}\n`;
}

function escapeRegExp(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function renderAutoLessonEntry(group) {
  return [
    `<!-- auto-lesson:${group.pattern} -->`,
    `## ${group.pattern}`,
    '',
    `- Frequency: ${group.frequency} events in the last 7 days`,
    `- Sources: ${group.sources.join(', ')}`,
    `- Example paths: ${renderExamples(group.examples)}`,
    `- First seen: ${group.firstSeen ?? '(unknown)'}`,
    `- Last seen: ${group.lastSeen ?? '(unknown)'}`,
    '- Status: candidate; human review is required before promotion.',
    '',
  ].join('\n');
}

function upsertSection(content, marker, section) {
  if (!content.includes(marker)) {
    return `${content.trimEnd()}\n\n${section}`;
  }
  const pattern = new RegExp(`${escapeRegExp(marker)}[\\s\\S]*?(?=\\n<!-- auto-lesson:|$)`);
  return content.replace(pattern, section.trimEnd());
}

async function writeAutoLessons(workspaceRoot, groups) {
  const autoLessonsPath = path.join(workspaceRoot, AUTO_LESSONS_FILE);
  await fs.mkdir(path.dirname(autoLessonsPath), { recursive: true });
  let content;
  try {
    content = await fs.readFile(autoLessonsPath, 'utf8');
  } catch (error) {
    if (error?.code !== 'ENOENT') {
      throw error;
    }
    content = [
      '# Auto Lessons',
      '',
      'Generated candidate lessons from local hook metrics.',
      'Do not promote entries to `.agents/rules/common-mistakes.md` without human review.',
      '',
    ].join('\n');
  }

  let nextContent = content;
  for (const group of groups.filter((item) => item.frequency >= PROMOTION_THRESHOLD)) {
    const marker = `<!-- auto-lesson:${group.pattern} -->`;
    nextContent = upsertSection(nextContent, marker, renderAutoLessonEntry(group));
  }

  await fs.writeFile(
    autoLessonsPath,
    nextContent.endsWith('\n') ? nextContent : `${nextContent}\n`,
  );
}

export async function runLessonsDigest({
  workspaceRoot = WORKSPACE_ROOT,
  now = createStableDigestWindowEnd(),
} = {}) {
  const groups = await summarizeLessonSignals({ workspaceRoot, now });
  const digestPath = path.join(workspaceRoot, WEEKLY_DIGEST_FILE);
  await fs.mkdir(path.dirname(digestPath), { recursive: true });
  await fs.writeFile(digestPath, renderWeeklyDigest(groups, { now }));
  await writeAutoLessons(workspaceRoot, groups);
  return {
    groups,
    weeklyDigestPath: digestPath,
    autoLessonsPath: path.join(workspaceRoot, AUTO_LESSONS_FILE),
  };
}
