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

const ROOT = path.resolve(__dirname, '..');
const CONTEXT_DIR = path.join(ROOT, '.qa-context');

function ensureDir(dir) {
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

function readJSON(filePath) {
  if (!fs.existsSync(filePath)) return null;
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  } catch { return null; }
}

function writeJSON(filePath, data) {
  ensureDir(path.dirname(filePath));
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
}

function pipelinePath() { return path.join(CONTEXT_DIR, 'pipeline.json'); }
function selectorsPath() { return path.join(CONTEXT_DIR, 'selectors.json'); }
function healHistoryPath() { return path.join(CONTEXT_DIR, 'heal-history.json'); }
function traceabilityPath() { return path.join(CONTEXT_DIR, 'traceability.json'); }

const context = {

  // ── Pipeline State ──

  getPipeline() {
    return readJSON(pipelinePath()) || {
      project_path: '',
      phases: { plan: { completed: [], in_progress: null }, generate: { completed: [], in_progress: null }, execute: { completed: [], in_progress: null }, heal: { completed: [], in_progress: null }, report: { completed: [], in_progress: null } },
      current_story: null,
      last_run_id: null,
      updated_at: '',
    };
  },

  setPipeline(updates) {
    const data = this.getPipeline();
    Object.assign(data, updates);
    data.updated_at = new Date().toISOString();
    writeJSON(pipelinePath(), data);
  },

  phaseComplete(phase, item) {
    const data = this.getPipeline();
    if (!data.phases[phase]) data.phases[phase] = { completed: [], in_progress: null };
    if (!data.phases[phase].completed.includes(item)) {
      data.phases[phase].completed.push(item);
    }
    data.phases[phase].in_progress = null;
    data.updated_at = new Date().toISOString();
    writeJSON(pipelinePath(), data);
  },

  phaseInProgress(phase, item) {
    const data = this.getPipeline();
    if (!data.phases[phase]) data.phases[phase] = { completed: [], in_progress: null };
    data.phases[phase].in_progress = item;
    data.updated_at = new Date().toISOString();
    writeJSON(pipelinePath(), data);
  },

  setCurrentStory(story) {
    this.setPipeline({ current_story: story });
  },

  setLastRun(runId) {
    this.setPipeline({ last_run_id: runId });
  },

  // ── Selectors Memory ──

  getSelectors() {
    return readJSON(selectorsPath()) || [];
  },

  addSelector(page, element, selectorValue, options = {}) {
    const selectors = this.getSelectors();
    let entry = selectors.find(s => s.page === page && s.element === element);
    if (!entry) {
      entry = { page, element, selectors: [], recommended: selectorValue };
      selectors.push(entry);
    }
    const existing = entry.selectors.find(s => s.value === selectorValue);
    if (existing) {
      existing.last_used = new Date().toISOString();
      if (options.reliability !== undefined) existing.reliability = options.reliability;
      if (options.healed) existing.healed = true;
      if (options.original) existing.original = options.original;
    } else {
      entry.selectors.push({
        value: selectorValue,
        type: options.type || 'css',
        reliability: options.reliability || 0.5,
        healed: options.healed || false,
        original: options.original || null,
        strategy: options.strategy || null,
        healed_at: options.healed_at || null,
        run_id: options.run_id || null,
        first_seen: new Date().toISOString(),
        last_used: new Date().toISOString(),
      });
    }
    entry.selectors.sort((a, b) => b.reliability - a.reliability);
    entry.recommended = entry.selectors[0].value;
    writeJSON(selectorsPath(), selectors);
  },

  getRecommendedSelector(page, element) {
    const selectors = this.getSelectors();
    const entry = selectors.find(s => s.page === page && s.element === element);
    return entry ? entry.recommended : null;
  },

  updateSelectorReliability(page, element, selectorValue, succeeded) {
    const selectors = this.getSelectors();
    const entry = selectors.find(s => s.page === page && s.element === element);
    if (!entry) return;
    const sel = entry.selectors.find(s => s.value === selectorValue);
    if (!sel) return;
    sel.reliability = succeeded
      ? Math.min(1, sel.reliability + 0.1)
      : Math.max(0, sel.reliability - 0.2);
    sel.last_used = new Date().toISOString();
    entry.selectors.sort((a, b) => b.reliability - a.reliability);
    entry.recommended = entry.selectors[0].value;
    writeJSON(selectorsPath(), selectors);
  },

  // ── Healing History ──

  getHealHistory() {
    return readJSON(healHistoryPath()) || [];
  },

  addHealAttempt(runId, story, test, element, attempts, success) {
    const history = this.getHealHistory();
    history.push({
      run_id: runId,
      story,
      test,
      element,
      original_selector: attempts[0] && attempts[0].original ? attempts[0].original : null,
      failure_reason: attempts[0] && attempts[0].error || null,
      attempts: attempts.map(a => ({
        selector: a.selector,
        strategy: a.strategy || 'retry',
        success: a.success,
        duration_ms: a.duration_ms || 0,
      })),
      success,
      duration_ms: attempts.reduce((s, a) => s + (a.duration_ms || 0), 0),
      timestamp: new Date().toISOString(),
    });
    writeJSON(healHistoryPath(), history);
    return history;
  },

  getHealHistoryForStory(story) {
    return this.getHealHistory().filter(h => h.story === story);
  },

  // ── Traceability ──

  getTraceability() {
    return readJSON(traceabilityPath()) || { stories: {} };
  },

  addRunId(story, runId, success, testName) {
    const data = this.getTraceability();
    if (!data.stories[story]) {
      data.stories[story] = { plan: null, spec: null, runs: [], latest_heal: null, status: 'pending' };
    }
    data.stories[story].runs.push({ run_id: runId, success, test: testName || null, timestamp: new Date().toISOString() });
    data.stories[story].status = success ? 'stable' : 'unstable';
    writeJSON(traceabilityPath(), data);
  },

  addPlan(story, planFile) {
    const data = this.getTraceability();
    if (!data.stories[story]) data.stories[story] = { plan: null, spec: null, runs: [], latest_heal: null, status: 'pending' };
    data.stories[story].plan = planFile;
    writeJSON(traceabilityPath(), data);
  },

  addSpec(story, specFile) {
    const data = this.getTraceability();
    if (!data.stories[story]) data.stories[story] = { plan: null, spec: null, runs: [], latest_heal: null, status: 'pending' };
    data.stories[story].spec = specFile;
    writeJSON(traceabilityPath(), data);
  },

  setHealed(story, runId) {
    const data = this.getTraceability();
    if (!data.stories[story]) data.stories[story] = { plan: null, spec: null, runs: [], latest_heal: null, status: 'pending' };
    data.stories[story].latest_heal = runId;
    writeJSON(traceabilityPath(), data);
  },

  // ── Init ──

  init() {
    ensureDir(CONTEXT_DIR);
    for (const file of ['pipeline.json', 'selectors.json', 'heal-history.json', 'traceability.json']) {
      const fp = path.join(CONTEXT_DIR, file);
      if (!fs.existsSync(fp)) {
        const defaults = {
          'pipeline.json': { project_path: '', phases: { plan: { completed: [], in_progress: null }, generate: { completed: [], in_progress: null }, execute: { completed: [], in_progress: null }, heal: { completed: [], in_progress: null }, report: { completed: [], in_progress: null } }, current_story: null, last_run_id: null, updated_at: '' },
          'selectors.json': [],
          'heal-history.json': [],
          'traceability.json': { stories: {} },
        };
        writeJSON(fp, defaults[file]);
      }
    }
  },

  contextDir: CONTEXT_DIR,
};

module.exports = context;
