import { spawn } from 'node:child_process';
import type { Context, MiddlewareFn } from 'telegraf';
import { env } from './env.js';
import { routePrompt } from './provider-router.js';

interface DynamicCommand {
  name: string;
  type: 'prompt' | 'bash';
  body: string;
  provider?: string;
}

interface CommandCache {
  commands: DynamicCommand[];
  fetchedAt: number;
}

const CACHE_TTL_MS = 30_000;
let cache: CommandCache | null = null;

async function fetchCommands(): Promise<DynamicCommand[]> {
  const endpoint = env.BOTMASTER_ENDPOINT;
  const botId = env.BOT_ID;
  if (!botId) return [];

  const url = `${endpoint}/api/commands/for-bot/${encodeURIComponent(botId)}`;
  const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
  if (!res.ok) throw new Error(`fetchCommands: HTTP ${res.status}`);
  return (await res.json()) as DynamicCommand[];
}

async function getCommands(): Promise<DynamicCommand[]> {
  const now = Date.now();
  if (cache && now - cache.fetchedAt < CACHE_TTL_MS) {
    return cache.commands;
  }

  try {
    const commands = await fetchCommands();
    cache = { commands, fetchedAt: now };
    return commands;
  } catch {
    // On fetch error, return stale cache if available (best-effort)
    if (cache) return cache.commands;
    return [];
  }
}

function extractCommandName(text: string): string | null {
  if (!text.startsWith('/')) return null;
  // Extract first word: /commandname@botname args → commandname
  const firstWord = text.slice(1).split(/\s+/)[0] ?? '';
  return firstWord.split('@')[0]!.toLowerCase() || null;
}

function runBash(body: string, userMessage: string): Promise<string> {
  return new Promise((resolve, reject) => {
    const child = spawn('bash', ['-c', body], {
      env: { ...process.env, MSG: userMessage },
    });

    const chunks: Buffer[] = [];
    child.stdout.on('data', (chunk: Buffer) => chunks.push(chunk));

    const errChunks: Buffer[] = [];
    child.stderr.on('data', (chunk: Buffer) => errChunks.push(chunk));

    child.on('error', reject);
    child.on('close', (code) => {
      if (code !== 0) {
        reject(new Error(`bash exited with code ${code}: ${Buffer.concat(errChunks).toString().trim()}`));
        return;
      }
      const output = Buffer.concat(chunks).toString();
      resolve(output.slice(0, 4000));
    });

    // Pipe user message to stdin
    child.stdin.end(userMessage, 'utf8');
  });
}

export function makeDynamicCommandHandler(): MiddlewareFn<Context> {
  return async (ctx, next) => {
    const msg = ctx.message;
    if (!msg || !('text' in msg)) return next();
    const text = msg.text.trim();
    const cmdName = extractCommandName(text);
    if (!cmdName) return next();

    const commands = await getCommands();
    const cmd = commands.find((c) => c.name.toLowerCase() === cmdName);
    if (!cmd) return next();

    // Extract user message: everything after the first word (the /command)
    const userMessage = text.replace(/^\/\S+\s*/, '');

    try {
      if (cmd.type === 'prompt') {
        const fullPrompt = `${cmd.body}\n\nUser: ${userMessage}`;
        const result = await routePrompt(
          cmd.name,
          fullPrompt,
          async (prompt) => {
            const apiKey = process.env.GOOGLE_API_KEY;
            const { generateTextCLI } = await import('./gemini/cli.js');
            return generateTextCLI(prompt, apiKey ?? undefined, cmd.name);
          },
        );
        await ctx.reply(result.text);
      } else if (cmd.type === 'bash') {
        const output = await runBash(cmd.body, userMessage);
        await ctx.reply(output || '(no output)');
      }
    } catch (err) {
      // Log but don't rethrow - reply with user-facing error
      console.error('[dynamic-commands] error:', err);
      await ctx.reply('Command failed. Check bot logs.');
    }
  };
}
