'use strict';

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

const provenance = require('./provenance');
const tracker = require('./tracker');
const versioning = require('./versioning');

const DAY_IN_MS = 24 * 60 * 60 * 1000;
const PENDING_AMENDMENT_STATUSES = Object.freeze(new Set(['pending', 'proposed', 'queued', 'open']));

function roundRate(value) {
  if (value === null) {
    return null;
  }

  return Math.round(value * 10000) / 10000;
}

function formatRate(value) {
  if (value === null) {
    return 'n/a';
  }

  return `${Math.round(value * 100)}%`;
}

function summarizeHealthReport(report) {
  const totalSkills = report.skills.length;
  const decliningSkills = report.skills.filter(skill => skill.declining).length;
  const healthySkills = totalSkills - decliningSkills;

  return {
    total_skills: totalSkills,
    healthy_skills: healthySkills,
    declining_skills: decliningSkills,
  };
}

function listSkillsInRoot(rootPath) {
  if (!rootPath || !fs.existsSync(rootPath)) {
    return [];
  }

  return fs.readdirSync(rootPath, { withFileTypes: true })
    .filter(entry => entry.isDirectory())
    .map(entry => ({
      skill_id: entry.name,
      skill_dir: path.join(rootPath, entry.name),
    }))
    .filter(entry => fs.existsSync(path.join(entry.skill_dir, 'SKILL.md')));
}

function discoverSkills(options = {}) {
  const roots = provenance.getSkillRoots(options);
  const discoveredSkills = [
    ...listSkillsInRoot(options.skillsRoot || roots.curated).map(skill => ({
      ...skill,
      skill_type: provenance.SKILL_TYPES.CURATED,
    })),
    ...listSkillsInRoot(options.learnedRoot || roots.learned).map(skill => ({
      ...skill,
      skill_type: provenance.SKILL_TYPES.LEARNED,
    })),
    ...listSkillsInRoot(options.importedRoot || roots.imported).map(skill => ({
      ...skill,
      skill_type: provenance.SKILL_TYPES.IMPORTED,
    })),
  ];

  return discoveredSkills.reduce((skillsById, skill) => {
    if (!skillsById.has(skill.skill_id)) {
      skillsById.set(skill.skill_id, skill);
    }
    return skillsById;
  }, new Map());
}

function calculateSuccessRate(records) {
  if (records.length === 0) {
    return null;
  }

  const successfulRecords = records.filter(record => record.outcome === 'success').length;
  return roundRate(successfulRecords / records.length);
}

function filterRecordsWithinDays(records, nowMs, days) {
  const cutoff = nowMs - (days * DAY_IN_MS);
  return records.filter(record => {
    const recordedAtMs = Date.parse(record.recorded_at);
    return !Number.isNaN(recordedAtMs) && recordedAtMs >= cutoff && recordedAtMs <= nowMs;
  });
}

function getFailureTrend(successRate7d, successRate30d, warnThreshold) {
  if (successRate7d === null || successRate30d === null) {
    return 'stable';
  }

  const delta = roundRate(successRate7d - successRate30d);
  if (delta <= (-1 * warnThreshold)) {
    return 'worsening';
  }

  if (delta >= warnThreshold) {
    return 'improving';
  }

  return 'stable';
}

function countPendingAmendments(skillDir) {
  if (!skillDir) {
    return 0;
  }

  return versioning.getEvolutionLog(skillDir, 'amendments')
    .filter(entry => {
      if (typeof entry.status === 'string') {
        return PENDING_AMENDMENT_STATUSES.has(entry.status);
      }

      return entry.event === 'proposal';
    })
    .length;
}

function getLastRun(records) {
  if (records.length === 0) {
    return null;
  }

  return records
    .map(record => ({
      timestamp: record.recorded_at,
      timeMs: Date.parse(record.recorded_at),
    }))
    .filter(entry => !Number.isNaN(entry.timeMs))
    .sort((left, right) => left.timeMs - right.timeMs)
    .at(-1)?.timestamp || null;
}

function collectSkillHealth(options = {}) {
  const now = options.now || new Date().toISOString();
  const nowMs = Date.parse(now);
  if (Number.isNaN(nowMs)) {
    throw new Error(`Invalid now timestamp: ${now}`);
  }

  const warnThreshold = typeof options.warnThreshold === 'number'
    ? options.warnThreshold
    : Number(options.warnThreshold || 0.1);
  if (!Number.isFinite(warnThreshold) || warnThreshold < 0) {
    throw new Error(`Invalid warn threshold: ${options.warnThreshold}`);
  }

  const records = tracker.readSkillExecutionRecords(options);
  const skillsById = discoverSkills(options);
  const recordsBySkill = records.reduce((groupedRecords, record) => {
    if (!groupedRecords.has(record.skill_id)) {
      groupedRecords.set(record.skill_id, []);
    }

    groupedRecords.get(record.skill_id).push(record);
    return groupedRecords;
  }, new Map());

  for (const skillId of recordsBySkill.keys()) {
    if (!skillsById.has(skillId)) {
      skillsById.set(skillId, {
        skill_id: skillId,
        skill_dir: null,
        skill_type: provenance.SKILL_TYPES.UNKNOWN,
      });
    }
  }

  const skills = Array.from(skillsById.values())
    .sort((left, right) => left.skill_id.localeCompare(right.skill_id))
    .map(skill => {
      const skillRecords = recordsBySkill.get(skill.skill_id) || [];
      const records7d = filterRecordsWithinDays(skillRecords, nowMs, 7);
      const records30d = filterRecordsWithinDays(skillRecords, nowMs, 30);
      const successRate7d = calculateSuccessRate(records7d);
      const successRate30d = calculateSuccessRate(records30d);
      const currentVersionNumber = skill.skill_dir ? versioning.getCurrentVersion(skill.skill_dir) : 0;
      const failureTrend = getFailureTrend(successRate7d, successRate30d, warnThreshold);

      return {
        skill_id: skill.skill_id,
        skill_type: skill.skill_type,
        current_version: currentVersionNumber > 0 ? `v${currentVersionNumber}` : null,
        pending_amendments: countPendingAmendments(skill.skill_dir),
        success_rate_7d: successRate7d,
        success_rate_30d: successRate30d,
        failure_trend: failureTrend,
        declining: failureTrend === 'worsening',
        last_run: getLastRun(skillRecords),
        run_count_7d: records7d.length,
        run_count_30d: records30d.length,
      };
    });

  return {
    generated_at: now,
    warn_threshold: warnThreshold,
    skills,
  };
}

function formatHealthReport(report, options = {}) {
  if (options.json) {
    return `${JSON.stringify(report, null, 2)}\n`;
  }

  const summary = summarizeHealthReport(report);

  if (!report.skills.length) {
    return [
      'ECC skill health',
      `Generated: ${report.generated_at}`,
      '',
      'No skill execution records found.',
      '',
    ].join('\n');
  }

  const lines = [
    'ECC skill health',
    `Generated: ${report.generated_at}`,
    `Skills: ${summary.total_skills} total, ${summary.healthy_skills} healthy, ${summary.declining_skills} declining`,
    '',
    'skill            version   7d     30d    trend       pending   last run',
    '--------------------------------------------------------------------------',
  ];

  for (const skill of report.skills) {
    const statusLabel = skill.declining ? '!' : ' ';
    lines.push([
      `${statusLabel}${skill.skill_id}`.padEnd(16),
      String(skill.current_version || '-').padEnd(9),
      formatRate(skill.success_rate_7d).padEnd(6),
      formatRate(skill.success_rate_30d).padEnd(6),
      skill.failure_trend.padEnd(11),
      String(skill.pending_amendments).padEnd(9),
      skill.last_run || '-',
    ].join(' '));
  }

  return `${lines.join('\n')}\n`;
}

module.exports = {
  PENDING_AMENDMENT_STATUSES,
  calculateSuccessRate,
  collectSkillHealth,
  discoverSkills,
  filterRecordsWithinDays,
  formatHealthReport,
  summarizeHealthReport,
};
