/**
 * CodexCliInstaller - Codex CLI integration for claude-mem
 *
 * Uses transcript-only watching (no notify hook). The watcher infrastructure
 * already exists in src/services/transcripts/. This installer:
 *
 * 1. Writes/merges transcript-watch config to ~/.claude-mem/transcript-watch.json
 * 2. Sets up watch for ~/.codex/sessions/**\/*.jsonl using existing watcher
 * 3. Injects context via workspace-local AGENTS.md files (Codex reads these natively)
 *
 * Anti-patterns:
 *   - Does NOT add notify hooks -- transcript watching is sufficient
 *   - Does NOT modify existing transcript watcher infrastructure
 *   - Does NOT overwrite existing transcript-watch.json -- merges only
 */

import path from 'path';
import { homedir } from 'os';
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { logger } from '../../utils/logger.js';
import { replaceTaggedContent } from '../../utils/claude-md-utils.js';
import {
  DEFAULT_CONFIG_PATH,
  DEFAULT_STATE_PATH,
  SAMPLE_CONFIG,
} from '../transcripts/config.js';
import type { TranscriptWatchConfig, WatchTarget } from '../transcripts/types.js';

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const CODEX_DIR = path.join(homedir(), '.codex');
const CODEX_AGENTS_MD_PATH = path.join(CODEX_DIR, 'AGENTS.md');
const CLAUDE_MEM_DIR = path.join(homedir(), '.claude-mem');

/**
 * The watch name used to identify the Codex CLI entry in transcript-watch.json.
 * Must match the name in SAMPLE_CONFIG for merging to work correctly.
 */
const CODEX_WATCH_NAME = 'codex';

// ---------------------------------------------------------------------------
// Transcript Watch Config Merging
// ---------------------------------------------------------------------------

/**
 * Load existing transcript-watch.json, or return an empty config scaffold.
 * Never throws -- returns a valid empty config on any parse error.
 */
function loadExistingTranscriptWatchConfig(): TranscriptWatchConfig {
  const configPath = DEFAULT_CONFIG_PATH;

  if (!existsSync(configPath)) {
    return { version: 1, schemas: {}, watches: [], stateFile: DEFAULT_STATE_PATH };
  }

  try {
    const raw = readFileSync(configPath, 'utf-8');
    const parsed = JSON.parse(raw) as TranscriptWatchConfig;

    // Ensure required fields exist
    if (!parsed.version) parsed.version = 1;
    if (!parsed.watches) parsed.watches = [];
    if (!parsed.schemas) parsed.schemas = {};
    if (!parsed.stateFile) parsed.stateFile = DEFAULT_STATE_PATH;

    return parsed;
  } catch (parseError) {
    logger.error('SYSTEM', 'Corrupt transcript-watch.json, creating backup', { path: configPath }, parseError as Error);

    // Back up corrupt file
    const backupPath = `${configPath}.backup.${Date.now()}`;
    writeFileSync(backupPath, readFileSync(configPath));
    console.warn(`  Backed up corrupt transcript-watch.json to ${backupPath}`);

    return { version: 1, schemas: {}, watches: [], stateFile: DEFAULT_STATE_PATH };
  }
}

/**
 * Merge Codex watch configuration into existing transcript-watch.json.
 *
 * - If a watch with name 'codex' already exists, it is replaced in-place.
 * - If the 'codex' schema already exists, it is replaced in-place.
 * - All other watches and schemas are preserved untouched.
 */
function mergeCodexWatchConfig(existingConfig: TranscriptWatchConfig): TranscriptWatchConfig {
  const merged = { ...existingConfig };

  // Merge schemas: add/replace the codex schema
  merged.schemas = { ...merged.schemas };
  const codexSchema = SAMPLE_CONFIG.schemas?.[CODEX_WATCH_NAME];
  if (codexSchema) {
    merged.schemas[CODEX_WATCH_NAME] = codexSchema;
  }

  // Merge watches: add/replace the codex watch entry
  const codexWatchFromSample = SAMPLE_CONFIG.watches.find(
    (w: WatchTarget) => w.name === CODEX_WATCH_NAME,
  );

  if (codexWatchFromSample) {
    const existingWatchIndex = merged.watches.findIndex(
      (w: WatchTarget) => w.name === CODEX_WATCH_NAME,
    );

    if (existingWatchIndex !== -1) {
      // Replace existing codex watch in-place
      merged.watches[existingWatchIndex] = codexWatchFromSample;
    } else {
      // Append new codex watch
      merged.watches.push(codexWatchFromSample);
    }
  }

  return merged;
}

/**
 * Write the merged transcript-watch.json config atomically.
 */
function writeTranscriptWatchConfig(config: TranscriptWatchConfig): void {
  mkdirSync(CLAUDE_MEM_DIR, { recursive: true });
  writeFileSync(DEFAULT_CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
}

// ---------------------------------------------------------------------------
// Context Injection (AGENTS.md)
// ---------------------------------------------------------------------------

/**
 * Remove legacy claude-mem context from ~/.codex/AGENTS.md.
 * Codex now uses workspace-local AGENTS.md files to avoid cross-project bleed.
 * Preserves any existing user content outside the tags.
 */
function removeCodexAgentsMdContext(): void {
  try {
    if (!existsSync(CODEX_AGENTS_MD_PATH)) return;

    const content = readFileSync(CODEX_AGENTS_MD_PATH, 'utf-8');
    const startTag = '<claude-mem-context>';
    const endTag = '</claude-mem-context>';

    const startIdx = content.indexOf(startTag);
    const endIdx = content.indexOf(endTag);

    if (startIdx === -1 || endIdx === -1) return;

    const before = content.substring(0, startIdx).replace(/\n+$/, '');
    const after = content.substring(endIdx + endTag.length).replace(/^\n+/, '');
    const finalContent = (before + (after ? '\n\n' + after : '')).trim();

    if (finalContent) {
      writeFileSync(CODEX_AGENTS_MD_PATH, finalContent + '\n');
    } else {
      writeFileSync(CODEX_AGENTS_MD_PATH, '');
    }

    console.log(`  Removed legacy global context from ${CODEX_AGENTS_MD_PATH}`);
  } catch (error) {
    logger.warn('SYSTEM', 'Failed to clean AGENTS.md context', { error: (error as Error).message });
  }
}

/**
 * @deprecated Codex now uses workspace-local AGENTS.md via transcript processor fallback.
 * Preserves user content outside the <claude-mem-context> tags.
 */
const cleanupLegacyCodexAgentsMdContext = removeCodexAgentsMdContext;

// ---------------------------------------------------------------------------
// Public API: Install
// ---------------------------------------------------------------------------

/**
 * Install Codex CLI integration for claude-mem.
 *
 * 1. Merges Codex transcript-watch config into ~/.claude-mem/transcript-watch.json
 * 2. Cleans up any legacy global context block in ~/.codex/AGENTS.md
 *
 * @returns 0 on success, 1 on failure
 */
export async function installCodexCli(): Promise<number> {
  console.log('\nInstalling Claude-Mem for Codex CLI (transcript watching)...\n');

  try {
    // Step 1: Merge transcript-watch config
    const existingConfig = loadExistingTranscriptWatchConfig();
    const mergedConfig = mergeCodexWatchConfig(existingConfig);
    writeTranscriptWatchConfig(mergedConfig);
    console.log(`  Updated ${DEFAULT_CONFIG_PATH}`);
    console.log(`  Watch path: ~/.codex/sessions/**/*.jsonl`);
    console.log(`  Schema: codex (v${SAMPLE_CONFIG.schemas?.codex?.version ?? '?'})`);

    // Step 2: Clean up legacy global AGENTS.md context
    cleanupLegacyCodexAgentsMdContext();

    console.log(`
Installation complete!

Transcript watch config: ${DEFAULT_CONFIG_PATH}
Context files: <workspace>/AGENTS.md

How it works:
  - claude-mem watches Codex session JSONL files for new activity
  - No hooks needed -- transcript watching is fully automatic
  - Context from past sessions is injected via AGENTS.md in the active Codex workspace

Next steps:
  1. Start claude-mem worker: npx claude-mem start
  2. Use Codex CLI as usual -- memory capture is automatic!
`);

    return 0;
  } catch (error) {
    console.error(`\nInstallation failed: ${(error as Error).message}`);
    return 1;
  }
}

// ---------------------------------------------------------------------------
// Public API: Uninstall
// ---------------------------------------------------------------------------

/**
 * Remove Codex CLI integration from claude-mem.
 *
 * 1. Removes the codex watch and schema from transcript-watch.json (preserves others)
 * 2. Removes context section from AGENTS.md (preserves user content)
 *
 * @returns 0 on success, 1 on failure
 */
export function uninstallCodexCli(): number {
  console.log('\nUninstalling Claude-Mem Codex CLI integration...\n');

  try {
    // Step 1: Remove codex watch from transcript-watch.json
    if (existsSync(DEFAULT_CONFIG_PATH)) {
      const config = loadExistingTranscriptWatchConfig();

      // Remove codex watch
      config.watches = config.watches.filter(
        (w: WatchTarget) => w.name !== CODEX_WATCH_NAME,
      );

      // Remove codex schema
      if (config.schemas) {
        delete config.schemas[CODEX_WATCH_NAME];
      }

      writeTranscriptWatchConfig(config);
      console.log(`  Removed codex watch from ${DEFAULT_CONFIG_PATH}`);
    } else {
      console.log('  No transcript-watch.json found -- nothing to remove.');
    }

    // Step 2: Remove legacy global context section from AGENTS.md
    cleanupLegacyCodexAgentsMdContext();

    console.log('\nUninstallation complete!');
    console.log('Restart claude-mem worker to apply changes.\n');

    return 0;
  } catch (error) {
    console.error(`\nUninstallation failed: ${(error as Error).message}`);
    return 1;
  }
}

// ---------------------------------------------------------------------------
// Public API: Status Check
// ---------------------------------------------------------------------------

/**
 * Check Codex CLI integration status.
 *
 * @returns 0 always (informational)
 */
export function checkCodexCliStatus(): number {
  console.log('\nClaude-Mem Codex CLI Integration Status\n');

  // Check transcript-watch.json
  if (!existsSync(DEFAULT_CONFIG_PATH)) {
    console.log('Status: Not installed');
    console.log(`  No transcript watch config at ${DEFAULT_CONFIG_PATH}`);
    console.log('\nRun: npx claude-mem install --ide codex-cli\n');
    return 0;
  }

  try {
    const config = loadExistingTranscriptWatchConfig();
    const codexWatch = config.watches.find(
      (w: WatchTarget) => w.name === CODEX_WATCH_NAME,
    );
    const codexSchema = config.schemas?.[CODEX_WATCH_NAME];

    if (!codexWatch) {
      console.log('Status: Not installed');
      console.log('  transcript-watch.json exists but no codex watch configured.');
      console.log('\nRun: npx claude-mem install --ide codex-cli\n');
      return 0;
    }

    console.log('Status: Installed');
    console.log(`  Config: ${DEFAULT_CONFIG_PATH}`);
    console.log(`  Watch path: ${codexWatch.path}`);
    console.log(`  Schema: ${codexSchema ? `codex (v${codexSchema.version ?? '?'})` : 'missing'}`);
    console.log(`  Start at end: ${codexWatch.startAtEnd ?? false}`);

    // Check context config
    if (codexWatch.context) {
      console.log(`  Context mode: ${codexWatch.context.mode}`);
      console.log(`  Context path: ${codexWatch.context.path ?? '<workspace>/AGENTS.md (default)'}`);
      console.log(`  Context updates on: ${codexWatch.context.updateOn?.join(', ') ?? 'none'}`);
    }

    // Check legacy global AGENTS.md usage
    if (existsSync(CODEX_AGENTS_MD_PATH)) {
      const mdContent = readFileSync(CODEX_AGENTS_MD_PATH, 'utf-8');
      if (mdContent.includes('<claude-mem-context>')) {
        console.log(`  Legacy global context: Present (${CODEX_AGENTS_MD_PATH})`);
      } else {
        console.log(`  Legacy global context: Not active`);
      }
    } else {
      console.log(`  Legacy global context: None`);
    }

    // Check if ~/.codex/sessions exists (indicates Codex has been used)
    const sessionsDir = path.join(CODEX_DIR, 'sessions');
    if (existsSync(sessionsDir)) {
      console.log(`  Sessions directory: exists`);
    } else {
      console.log(`  Sessions directory: not yet created (use Codex CLI to generate sessions)`);
    }
  } catch {
    console.log('Status: Unknown');
    console.log('  Could not parse transcript-watch.json.');
  }

  console.log('');
  return 0;
}
