import type { AgentConfigRegistry } from '../../agents/registry.js';
import { mkdirSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { resolve } from 'node:path';
import type { ChatResponseFormat } from '../../models/types.js';
import type { Tool, ToolResult } from '../types.js';
import { CouncilsOrchestrator, type CouncilsConfig } from '../../councils/orchestrator.js';
import type { CouncilScaffold } from '../../councils/scaffold.js';
import { councilRunInputSchema } from '../../councils/types.js';

const COUNCIL_TOOL_TIMEOUT_MS = 180_000;

interface DelegateRunner {
  delegate(request: {
    tier: 'fast' | 'default' | 'complex' | 'local';
    systemPrompt: string;
    message: string;
    maxTokens?: number;
    responseFormat?: ChatResponseFormat;
  }): Promise<{
    content: string;
    usage: { inputTokens: number; outputTokens: number };
    tier: 'fast' | 'default' | 'complex' | 'local';
  }>;
}

export interface CouncilRunDeps {
  registry: AgentConfigRegistry;
  orchestrator: DelegateRunner;
  config: CouncilsConfig;
  scaffold?: CouncilScaffold;
}

function slugifyTask(task: string): string {
  const trimmed = task.trim().toLowerCase();
  if (!trimmed) {
    return 'council-run';
  }
  const slug = trimmed
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 80);
  return slug || 'council-run';
}

function formatTimestamp(date: Date): string {
  return date.toISOString().replace(/[:.]/g, '-');
}

function getCouncilsDir(): string {
  const dataDir = process.env.FLYNN_DATA_DIR ?? resolve(homedir(), '.local/share/flynn');
  return resolve(dataDir, 'councils');
}

function writeCouncilArtifacts(
  task: string,
  summaryLines: string[],
  conversationLog: string,
  resultJson: string,
): { jsonPath: string; markdownPath: string } {
  const dir = getCouncilsDir();
  mkdirSync(dir, { recursive: true });

  const stamp = formatTimestamp(new Date());
  const base = `${stamp}-${slugifyTask(task)}`;
  const jsonPath = resolve(dir, `${base}.json`);
  const markdownPath = resolve(dir, `${base}.md`);

  const markdown = [
    ...summaryLines,
    '',
    'Conversations:',
    '',
    conversationLog || '(none)',
    '',
    'Raw Result JSON:',
    '```json',
    resultJson,
    '```',
    '',
  ].join('\n');

  writeFileSync(jsonPath, `${resultJson}\n`, 'utf-8');
  writeFileSync(markdownPath, markdown, 'utf-8');
  return { jsonPath, markdownPath };
}

function classifyCouncilFailure(message: string): 'network_or_latency' | 'json_format' | 'config' | 'cap_overflow' | 'user_cancelled' | 'unknown' {
  const lower = message.toLowerCase();
  if (
    lower.includes('operation cancelled by user')
    || lower.includes('aborterror')
    || lower.includes('aborted')
    || lower.includes('cancelled')
  ) {
    return 'user_cancelled';
  }
  if (
    lower.includes('connection error')
    || lower.includes('timed out')
    || lower.includes('econn')
    || lower.includes('enotfound')
    || lower.includes('all model providers failed')
  ) {
    return 'network_or_latency';
  }
  if (
    lower.includes('repair_failed')
    || lower.includes('parse_failed')
    || lower.includes('json')
  ) {
    return 'json_format';
  }
  if (
    lower.includes('not configured')
    || lower.includes('disabled')
    || lower.includes('meta_validation_failed')
    || lower.includes('grounding_failed')
    || lower.includes('bridge_validation_failed')
  ) {
    return 'config';
  }
  if (
    lower.includes('cap_exceeded')
    || lower.includes('cap_top_ideas')
    || lower.includes('cap_field_bullets')
    || lower.includes('cap_entry_chars')
    || lower.includes('cap_total_chars')
  ) {
    return 'cap_overflow';
  }
  return 'unknown';
}

function buildFailureHint(kind: ReturnType<typeof classifyCouncilFailure>): string {
  switch (kind) {
    case 'network_or_latency':
      return 'Likely network/provider latency issue. Check endpoint reachability and consider faster council tiers.';
    case 'json_format':
      return 'Likely model output-format issue. Council JSON repair/retry was unable to normalize output.';
    case 'config':
      return 'Likely councils/agent configuration issue. Verify council agent names, tiers, and strict validation settings.';
    case 'cap_overflow':
      return 'Bridge payload cap exceeded. Reduce task breadth, lower max rounds, or raise councils.defaults bridge cap settings.';
    case 'user_cancelled':
      return 'Run was cancelled by user input (Esc/Ctrl+C). Re-run the council task when ready.';
    default:
      return 'No deterministic diagnosis from error text.';
  }
}

export function createCouncilRunTool(deps: CouncilRunDeps): Tool {
  return {
    name: 'council.run',
    description:
      'Run the deterministic dual-council pipeline (D/P groups with bridge-only exchange and meta merge).',
    timeoutMs: COUNCIL_TOOL_TIMEOUT_MS,
    inputSchema: {
      type: 'object',
      properties: {
        task: { type: 'string', description: 'Primary task or question to explore' },
        constraints: { type: 'object', description: 'Optional constraints object (or pass string)' },
        success_definition: { type: 'string', description: 'What success looks like for this run' },
        budget: { type: 'object', description: 'Optional budget limits/time/cost constraints' },
        timebox: { type: 'string', description: 'Optional timebox (e.g. 30m)' },
        output_format: { type: 'string', description: 'Desired output format' },
        max_rounds: { type: 'number', description: 'Override configured max rounds (1-6)' },
        session_id: { type: 'string', description: 'Optional external session/run id' },
      },
      required: ['task'],
    },
    execute: async (rawArgs: unknown): Promise<ToolResult> => {
      try {
        const args = councilRunInputSchema.parse(rawArgs);
        const runner = new CouncilsOrchestrator({
          registry: deps.registry,
          orchestrator: deps.orchestrator,
          config: deps.config,
          scaffold: deps.scaffold,
        });
        const result = await runner.run(args);
        const timedTrace = result.trace.filter((event) => typeof event.latency_ms === 'number');
        const totalLatencyMs = timedTrace.reduce((sum, event) => sum + (event.latency_ms ?? 0), 0);
        const phaseLatency = new Map<number, number>();
        for (const event of timedTrace) {
          const phase = event.phase_index;
          phaseLatency.set(phase, (phaseLatency.get(phase) ?? 0) + (event.latency_ms ?? 0));
        }
        const phaseLatencyLines = [...phaseLatency.entries()]
          .sort((a, b) => a[0] - b[0])
          .map(([phase, latency]) => `- Phase ${phase}: ${latency}ms`);
        const slowestCalls = [...timedTrace]
          .sort((a, b) => (b.latency_ms ?? 0) - (a.latency_ms ?? 0))
          .slice(0, 5)
          .map((event) => `- ${event.call_id}: ${event.latency_ms ?? 0}ms`);

        const lines = [
          `[Council pipeline v${result.pipeline_version}]`,
          `Stop reason: ${result.stop_snapshot.stop_reason} (round ${result.stop_snapshot.round_reached})`,
          `D shortlist: ${result.stop_snapshot.final_shortlist_D.join(', ') || 'none'}`,
          `P shortlist: ${result.stop_snapshot.final_shortlist_P.join(', ') || 'none'}`,
          `Bridge validated: ${result.stop_snapshot.bridge_validated ? 'yes' : 'no'}`,
          `Grounding failures: ${result.stop_snapshot.grounding_failures_count}`,
          '',
          'Meta selection:',
          `- Primary: ${result.meta.selected_primary.join(', ') || 'none'}`,
          `- Secondary: ${result.meta.selected_secondary.join(', ') || 'none'}`,
          `- Open questions: ${result.meta.open_questions.length}`,
          `- Next experiments: ${result.meta.next_experiments.length}`,
          '',
          'Timing:',
          `- Timed calls: ${timedTrace.length}`,
          `- Total model latency (summed): ${totalLatencyMs}ms`,
          ...phaseLatencyLines,
          ...(
            slowestCalls.length > 0
              ? ['', 'Slowest calls:', ...slowestCalls]
              : []
          ),
          '',
          `Agent conversations: ${result.conversations.length}`,
        ];

        const conversationLog = result.conversations
          .map((turn, index) => {
            const prompt = JSON.stringify(turn.prompt_payload);
            return [
              `#${index + 1} ${turn.call_id} [${turn.agent} @ ${turn.tier}]`,
              `Prompt payload: ${prompt}`,
              `Response: ${turn.response}`,
            ].join('\n');
          })
          .join('\n\n');

        const resultJson = JSON.stringify(result, null, 2);
        const artifacts = writeCouncilArtifacts(args.task, lines, conversationLog, resultJson);

        return {
          success: true,
          output: [
            ...lines,
            '',
            'Artifacts:',
            `- Summary report: ${artifacts.markdownPath}`,
            `- Full JSON: ${artifacts.jsonPath}`,
          ].join('\n'),
        };
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        const kind = classifyCouncilFailure(message);
        const hint = buildFailureHint(kind);
        return {
          success: false,
          output: '',
          error: [
            message,
            `Likely root cause: ${kind}`,
            `Hint: ${hint}`,
            `Council config: D=${deps.config.groups.D.arbiter_agent}/${deps.config.groups.D.freethinker_agent}@${deps.config.groups.D.model_tier ?? 'agent-tier'}, ` +
              `P=${deps.config.groups.P.arbiter_agent}/${deps.config.groups.P.freethinker_agent}@${deps.config.groups.P.model_tier ?? 'agent-tier'}, ` +
              `meta=${deps.config.meta_arbiter_agent}@${deps.config.meta_model_tier ?? 'agent-tier'}`,
          ].join('\n'),
        };
      }
    },
  };
}
