'use strict';

const { normalizeAgentMode } = require('../agent-runners/contract');

function normalizeHarness(harness = {}) {
  const id = harness.id || harness.runnerId;
  if (!id) throw new Error('Harness id is required');
  const providerType = harness.providerType || harness.provider || 'unknown';
  const capabilities = {
    modes: ['build'],
    persistentSessions: false,
    streaming: false,
    codeEditing: true,
    reviewOnly: false,
    sandboxModes: [],
    supportsImages: false,
    supportsPatch: true,
    ...(harness.capabilities || {}),
  };
  return {
    ...harness,
    id,
    runnerId: harness.runnerId || id,
    providerType,
    kind: harness.kind || 'cli-agent',
    priority: Number.isFinite(Number(harness.priority)) ? Number(harness.priority) : 0,
    capabilities,
  };
}

function createHarnessFromRunner(runner = {}, extra = {}) {
  const normalized = normalizeHarness({
    id: runner.id,
    runnerId: runner.id,
    providerType: runner.providerType,
    kind: runner.kind || 'cli-agent',
    capabilities: {
      modes: runner.capabilities?.modes || ['plan', 'build', 'review', 'analysis'],
      persistentSessions: Boolean(runner.capabilities?.resume),
      streaming: Boolean(runner.capabilities?.streamJson),
      codeEditing: true,
      reviewOnly: false,
      sandboxModes: runner.capabilities?.sandboxModes || [],
      supportsImages: Boolean(runner.capabilities?.images),
      supportsPatch: runner.capabilities?.patch !== false,
      ...(runner.capabilities || {}),
    },
    run: async (request) => runner.runTask(request),
    ...extra,
  });
  return normalized;
}

class AgentHarnessRegistry {
  constructor() {
    this.harnesses = new Map();
  }

  registerHarness(harness) {
    const normalized = normalizeHarness(harness);
    if (typeof normalized.run !== 'function' && !normalized.runnerId) {
      throw new Error(`Harness ${normalized.id} needs a run function or runnerId`);
    }
    this.harnesses.set(normalized.id, normalized);
    return normalized;
  }

  getHarness(id) {
    return this.harnesses.get(id) || null;
  }

  listHarnesses() {
    return [...this.harnesses.values()].map((harness) => ({
      id: harness.id,
      runnerId: harness.runnerId,
      providerType: harness.providerType,
      kind: harness.kind,
      priority: harness.priority,
      capabilities: harness.capabilities,
    }));
  }

  candidates() {
    return [...this.harnesses.values()];
  }
}

function createDefaultHarnessRegistry({ runners = null } = {}) {
  const registry = new AgentHarnessRegistry();
  const source = runners || lazyListRunners();
  for (const runner of source) {
    registry.registerHarness(createHarnessFromRunner(runner));
  }
  return registry;
}

function harnessSupportsRequest(harness, request = {}) {
  const normalized = normalizeHarness(harness);
  const mode = normalizeAgentMode(request.mode || 'build');
  const modes = normalized.capabilities.modes || [];
  if (modes.length > 0 && !modes.includes(mode)) return false;
  if (normalized.capabilities.reviewOnly && mode !== 'review') return false;
  if (request.providerType && normalized.providerType !== request.providerType) return false;
  if (request.provider && normalized.providerType !== request.provider) return false;
  if (request.runnerId && normalized.runnerId !== request.runnerId && normalized.id !== request.runnerId) return false;
  if (request.harnessId && normalized.id !== request.harnessId) return false;
  if (request.sandbox && Array.isArray(normalized.capabilities.sandboxModes) && normalized.capabilities.sandboxModes.length > 0) {
    if (!normalized.capabilities.sandboxModes.includes(request.sandbox)) return false;
  }
  return true;
}

function scoreHarness(harness, request = {}, opts = {}) {
  const normalized = normalizeHarness(harness);
  let score = 0.5 + normalized.priority / 100;
  const mode = normalizeAgentMode(request.mode || 'build');
  if (request.harnessId === normalized.id) score += 0.35;
  if (request.runnerId && (request.runnerId === normalized.runnerId || request.runnerId === normalized.id)) score += 0.20;
  if (request.providerType === normalized.providerType || request.provider === normalized.providerType) score += 0.15;
  if (request.model && normalized.model === request.model) score += 0.05;
  if (mode === 'review' && normalized.providerType && request.builderProviderType && normalized.providerType !== request.builderProviderType) score += 0.08;
  if (normalized.capabilities.persistentSessions) score += request.persistent ? 0.08 : 0.02;
  if (normalized.capabilities.streaming) score += 0.02;

  const availability = opts.providerAvailability;
  if (availability?.isProviderUsable) {
    if (!availability.isProviderUsable(normalized.providerType)) score -= 0.40;
  } else if (availability?.getProviderState) {
    const state = availability.getProviderState(normalized.providerType);
    if (state?.status === 'unhealthy') score -= 0.40;
  }

  const quality = Number(opts.qualityScore ?? normalized.qualityScore);
  if (Number.isFinite(quality)) score += (quality - 0.5) * 0.25;

  return Math.max(0, Math.min(1, score));
}

function selectAgentHarness(request = {}, candidates = [], opts = {}) {
  const list = candidates.length > 0
    ? candidates.map(normalizeHarness)
    : (opts.registry ? opts.registry.candidates() : []);
  const supported = list.filter(harness => harnessSupportsRequest(harness, request));
  const scored = supported.map((harness, index) => ({
    harness,
    score: scoreHarness(harness, request, opts),
    index,
  }));
  scored.sort((a, b) => {
    if (b.score !== a.score) return b.score - a.score;
    if (b.harness.priority !== a.harness.priority) return b.harness.priority - a.harness.priority;
    return a.index - b.index;
  });
  const winner = scored[0] || null;
  return {
    selected: winner?.harness || null,
    candidates: scored.map(entry => ({
      ...entry.harness,
      score: entry.score,
    })),
    reason: winner ? 'selected-highest-scoring-supported-harness' : 'no-supported-harness',
  };
}

async function runSelectedHarness(request = {}, opts = {}) {
  const registry = opts.registry || createDefaultHarnessRegistry(opts);
  const selection = selectAgentHarness(request, [], { ...opts, registry });
  if (!selection.selected) {
    return {
      success: false,
      error: selection.reason,
      selection,
    };
  }
  const harness = selection.selected;
  if (typeof harness.run === 'function') {
    const result = await harness.run({
      ...request,
      runnerId: harness.runnerId,
      providerType: harness.providerType,
    });
    return { ...result, harnessId: harness.id, selection };
  }
  const runner = opts.runTask || lazyRunAgentTask();
  const result = await runner(harness.runnerId, {
    ...request,
    providerType: harness.providerType,
  });
  return { ...result, harnessId: harness.id, selection };
}

function lazyListRunners() {
  const runners = require('../agent-runners');
  return ['claude-code', 'codex', 'gemini']
    .map(id => runners.getAgentRunner(id))
    .filter(Boolean);
}

function lazyRunAgentTask() {
  return require('../agent-runners').runAgentTask;
}

module.exports = {
  AgentHarnessRegistry,
  createHarnessFromRunner,
  createDefaultHarnessRegistry,
  harnessSupportsRequest,
  normalizeHarness,
  runSelectedHarness,
  scoreHarness,
  selectAgentHarness,
};
