import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { createInterface } from 'node:readline';

import { Command } from 'commander';
import pc from 'picocolors';

import { resolveAdapter } from '../lib/adapter.js';
import { addChatLog } from '../lib/chat-log-store.js';
import { loadConfig, getDataDir, ensureDataDir } from '../lib/config.js';
import { buildSystemPromptContext } from '../lib/context-builder.js';
import { addHistoryEntry } from '../lib/history-store.js';
import type { SessionInfo, ProviderName } from '../lib/types.js';

const SESSION_FILE = 'current-session.json';

async function loadSession(): Promise<SessionInfo | null> {
  try {
    const raw = await readFile(join(getDataDir(), SESSION_FILE), 'utf-8');
    return JSON.parse(raw) as SessionInfo;
  } catch {
    return null;
  }
}

async function saveSession(session: SessionInfo): Promise<void> {
  await ensureDataDir();
  await writeFile(
    join(getDataDir(), SESSION_FILE),
    JSON.stringify(session, null, 2) + '\n',
    'utf-8',
  );
}

/** Create the `dproxy chat` Commander command for interactive conversations. */
export function createChatCommand(): Command {
  return new Command('chat')
    .description('Start an interactive conversation')
    .option('-p, --provider <provider>', 'Provider to use (claude, codex, gemini, opencode)')
    .option('-c, --continue', 'Continue last conversation')
    .option('-r, --resume <id>', 'Resume a specific session')
    .option('-m, --model <model>', 'Model to use')
    .option('--max-turns <n>', 'Max agent turns per message', parseInt)
    .option('--no-memory', 'Skip memory injection')
    .option('--no-life', 'Skip life/PARA context injection')
    .action(async (opts) => {
      try {
        await runChat(opts);
      } catch (err) {
        console.error(pc.red((err as Error).message));
        process.exit(1);
      }
    });
}

/** Run the interactive chat REPL: readline loop with session tracking and context injection. */
async function runChat(opts: Record<string, unknown>): Promise<void> {
  const config = await loadConfig();

  // Resolve the adapter
  const providerName = (opts.provider as ProviderName | undefined) ?? config.provider.default;
  const adapter = resolveAdapter(providerName, config);

  let sessionId: string | undefined;

  // Resume or continue
  if (opts.resume) {
    sessionId = opts.resume as string;
    console.log(pc.dim(`Resuming session: ${sessionId}`));
  } else if (opts.continue) {
    const prev = await loadSession();
    if (prev) {
      sessionId = prev.sessionId;
      console.log(pc.dim(`Continuing session: ${sessionId}`));
    } else {
      console.log(pc.dim('No previous session found. Starting new chat.'));
    }
  }

  // Build context via shared builder (same sources as ask)
  const appendSystemPrompt =
    (await buildSystemPromptContext(
      {
        memory: opts.memory !== false,
        life: opts.life !== false,
        workspace: true,
        chatLog: true,
      },
      config,
    )) || undefined;

  console.log(pc.bold(pc.blue(`${adapter.provider} Chat`)));
  console.log(pc.dim('Type "exit" or Ctrl+C to quit.\n'));

  const rl = createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: pc.green('you > '),
  });

  rl.prompt();

  rl.on('line', async (line: string) => {
    const input = line.trim();
    if (!input) {
      rl.prompt();
      return;
    }

    if (input === 'exit' || input === 'quit') {
      console.log(pc.dim('Bye!'));
      rl.close();
      return;
    }

    // Lock input while processing
    rl.pause();

    try {
      process.stdout.write(pc.dim('thinking...\r'));

      const result = await adapter.execute({
        prompt: input,
        model: (opts.model as string) ?? config.defaults.model,
        maxTurns: (opts.maxTurns as number) ?? config.defaults.maxTurns,
        sessionId,
        options: { appendSystemPrompt },
      });

      // Clear "thinking..." and print response
      process.stdout.write('\r' + ' '.repeat(20) + '\r');
      console.log(pc.cyan(`${adapter.provider} > `) + result.text);
      console.log();

      // Capture session ID from first response
      if (!sessionId && result.sessionId) {
        sessionId = result.sessionId;
        await saveSession({
          sessionId,
          startedAt: new Date().toISOString(),
        });
      }

      // Save to history
      await addHistoryEntry({
        prompt: input,
        result: result.text,
        sessionId: result.sessionId ?? '',
        costUsd: result.costUsd ?? 0,
        durationMs: result.durationMs,
        model: (opts.model as string) ?? config.defaults.model,
      });

      // Save to daily chat log
      void addChatLog(input, result.text);
    } catch (err) {
      console.error(pc.red((err as Error).message));
    }

    // Unlock input
    rl.resume();
    rl.prompt();
  });

  rl.on('close', () => {
    process.exit(0);
  });
}
