import { readFile, writeFile, appendFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { resolve } from "node:path";

export interface UserCorrectionRecord {
  id: string;
  record_type: 'conversation';
  action_type: 'user_correction';
  priority: 'critical_for_recovery';
  timestamp: string;
  original_claim: string;
  correction: string;
  principle_extracted: string;
  persisted_to: string[];
}

const CORRECTIONS_REL = '.boo/corrections/index.json';

function correctionsDir(basePath?: string): string {
  return resolve(basePath ?? process.cwd(), '.boo/corrections');
}

function correctionsPath(basePath?: string): string {
  return resolve(basePath ?? process.cwd(), CORRECTIONS_REL);
}

function tryParseJson<T>(raw: string): T | null {
  try {
    return JSON.parse(raw) as T;
  } catch {
    return null;
  }
}

export interface CorrectionsIndex {
  corrections: UserCorrectionRecord[];
}

async function readCorrections(basePath?: string): Promise<CorrectionsIndex> {
  try {
    const raw = await readFile(correctionsPath(basePath), 'utf-8');
    return tryParseJson<CorrectionsIndex>(raw) ?? { corrections: [] };
  } catch {
    return { corrections: [] };
  }
}

async function writeCorrections(idx: CorrectionsIndex, basePath?: string): Promise<void> {
  const dir = correctionsDir(basePath);
  if (!existsSync(dir)) {
    const { mkdir } = await import('node:fs/promises');
    await mkdir(dir, { recursive: true });
  }
  await writeFile(correctionsPath(basePath), JSON.stringify(idx, null, 2), 'utf-8');
}

let idCounter = 0;

function nextId(): string {
  idCounter++;
  return `uc_${Date.now()}_${idCounter}`;
}

function isoNow(): string {
  return new Date().toISOString();
}

/**
 * Record a user correction. Stores it in .boo/corrections/index.json
 * and returns the record with the assigned id.
 */
export async function recordCorrection(
  originalClaim: string,
  correction: string,
  principleExtracted: string,
  persistedTo: string[] = [],
  basePath?: string,
): Promise<UserCorrectionRecord> {
  const idx = await readCorrections(basePath);
  const record: UserCorrectionRecord = {
    id: nextId(),
    record_type: 'conversation',
    action_type: 'user_correction',
    priority: 'critical_for_recovery',
    timestamp: isoNow(),
    original_claim: originalClaim,
    correction,
    principle_extracted: principleExtracted,
    persisted_to: persistedTo,
  };
  idx.corrections.push(record);
  await writeCorrections(idx, basePath);
  return record;
}

/**
 * Scan an audit_trail.jsonl file for user_correction records.
 * Returns all matching records found in the file.
 */
export async function scanForCorrections(
  auditPath: string,
): Promise<UserCorrectionRecord[]> {
  try {
    const raw = await readFile(auditPath, 'utf-8');
    const lines = raw.split('\n').filter(Boolean);
    const corrections: UserCorrectionRecord[] = [];
    for (const line of lines) {
      const record = tryParseJson<Record<string, unknown>>(line);
      if (record?.action_type === 'user_correction') {
        corrections.push(record as unknown as UserCorrectionRecord);
      }
    }
    return corrections;
  } catch {
    return [];
  }
}

/**
 * Check if a proposed action contradicts any known user correction.
 * Returns an array of contradiction warnings — empty means no contradictions.
 */
export function checkContradiction(
  action: string,
  corrections: UserCorrectionRecord[],
): { contradicts: boolean; warnings: { correction: UserCorrectionRecord; reason: string }[] } {
  const warnings: { correction: UserCorrectionRecord; reason: string }[] = [];

  for (const c of corrections) {
    // Check if the action mentions the original claim's topic
    const actionLower = action.toLowerCase();
    const claimFragments = c.original_claim.toLowerCase().split(/\s+/).filter((w) => w.length > 4);

    // If any significant word from the original claim appears in the proposed action,
    // flag this as a potential contradiction
    const matchingFragments = claimFragments.filter((f) => actionLower.includes(f));
    if (matchingFragments.length >= 2) {
      warnings.push({
        correction: c,
        reason: `Action "${action.slice(0, 60)}" may contradict prior correction: "${c.original_claim}" → "${c.correction}" (principle: ${c.principle_extracted})`,
      });
    }
  }

  return {
    contradicts: warnings.length > 0,
    warnings,
  };
}

/**
 * Add a file path to a correction's persisted_to array.
 */
export async function markPersisted(
  correctionId: string,
  filePath: string,
  basePath?: string,
): Promise<UserCorrectionRecord | null> {
  const idx = await readCorrections(basePath);
  const record = idx.corrections.find((c) => c.id === correctionId);
  if (!record) return null;

  if (!record.persisted_to.includes(filePath)) {
    record.persisted_to.push(filePath);
  }
  await writeCorrections(idx, basePath);
  return record;
}

/**
 * Get all stored user corrections.
 */
export async function listCorrections(basePath?: string): Promise<UserCorrectionRecord[]> {
  const idx = await readCorrections(basePath);
  return idx.corrections;
}

/**
 * Append a correction record to an audit_trail.jsonl file (inline storage).
 */
export async function appendCorrectionToTrail(
  trailPath: string,
  correction: UserCorrectionRecord,
): Promise<void> {
  await appendFile(trailPath, JSON.stringify(correction) + '\n', 'utf-8');
}
