import {
  classifyRotationTrigger,
  type AgentTriggerReason,
  type CodexRotationReason,
  isCodexRotationReason,
} from './agent-error-detection.js';
import { detectCodexRotationTrigger } from './codex-token-rotation.js';
import { getErrorMessage } from './utils.js';

export interface AttemptStreamedTrigger {
  reason: AgentTriggerReason;
  retryAfterMs?: number;
  message?: string;
}

export interface AttemptRetryState {
  sawOutput: boolean;
  retryableSessionFailureDetected?: boolean;
  streamedTriggerReason?: AttemptStreamedTrigger;
  error?: unknown;
  outputError?: string | null;
}

export function isRetryableClaudeSessionFailureAttempt(args: {
  attempt: AttemptRetryState;
  isClaudeCodeAgent: boolean;
  provider: 'claude' | 'codex';
  shouldRetryFreshSessionOnAgentFailure: (args: {
    result: null;
    error: string;
  }) => boolean;
}): boolean {
  if (
    !args.isClaudeCodeAgent ||
    args.provider !== 'claude' ||
    args.attempt.sawOutput
  ) {
    return false;
  }

  if (args.attempt.retryableSessionFailureDetected === true) {
    return true;
  }

  if (args.attempt.error == null) {
    return false;
  }

  return args.shouldRetryFreshSessionOnAgentFailure({
    result: null,
    error: getErrorMessage(args.attempt.error),
  });
}

export function resolveClaudeRetryTrigger(args: {
  canRetryClaudeCredentials: boolean;
  provider: 'claude' | 'codex';
  attempt: Pick<AttemptRetryState, 'sawOutput' | 'streamedTriggerReason'>;
  fallbackMessage?: string | null;
}): { reason: AgentTriggerReason; retryAfterMs?: number } | null {
  if (
    !(
      args.canRetryClaudeCredentials &&
      args.provider === 'claude' &&
      !args.attempt.sawOutput
    )
  ) {
    return null;
  }

  if (args.attempt.streamedTriggerReason) {
    return {
      reason: args.attempt.streamedTriggerReason.reason,
      retryAfterMs: args.attempt.streamedTriggerReason.retryAfterMs,
    };
  }

  const trigger = classifyRotationTrigger(args.fallbackMessage);
  if (!trigger.shouldRetry) {
    return null;
  }

  return {
    reason: trigger.reason,
    retryAfterMs: trigger.retryAfterMs,
  };
}

export function resolveCodexRetryTrigger(args: {
  canRetryCodex: boolean;
  attempt: Pick<AttemptRetryState, 'streamedTriggerReason'>;
  rotationMessage?: string | null;
}): { reason: CodexRotationReason } | null {
  if (!args.canRetryCodex) {
    return null;
  }

  if (args.attempt.streamedTriggerReason) {
    if (!isCodexRotationReason(args.attempt.streamedTriggerReason.reason)) {
      return null;
    }
    return {
      reason: args.attempt.streamedTriggerReason.reason,
    };
  }

  const trigger = detectCodexRotationTrigger(args.rotationMessage);
  if (!trigger.shouldRotate) {
    return null;
  }

  return { reason: trigger.reason };
}

export type AttemptRetryAction =
  | {
      kind: 'claude';
      trigger: { reason: AgentTriggerReason; retryAfterMs?: number };
      rotationMessage?: string;
    }
  | {
      kind: 'codex';
      trigger: { reason: CodexRotationReason };
      rotationMessage?: string;
    }
  | { kind: 'none' };

export function resolveAttemptRetryAction(args: {
  provider: 'claude' | 'codex';
  canRetryClaudeCredentials: boolean;
  canRetryCodex: boolean;
  attempt: Pick<AttemptRetryState, 'sawOutput' | 'streamedTriggerReason'>;
  rotationMessage?: string | null;
}): AttemptRetryAction {
  const normalizedRotationMessage =
    args.rotationMessage ??
    args.attempt.streamedTriggerReason?.message ??
    undefined;

  const claudeTrigger = resolveClaudeRetryTrigger({
    canRetryClaudeCredentials: args.canRetryClaudeCredentials,
    provider: args.provider,
    attempt: args.attempt,
    fallbackMessage: normalizedRotationMessage,
  });
  if (claudeTrigger) {
    return {
      kind: 'claude',
      trigger: claudeTrigger,
      rotationMessage: normalizedRotationMessage,
    };
  }

  const codexTrigger = resolveCodexRetryTrigger({
    canRetryCodex: args.canRetryCodex,
    attempt: args.attempt,
    rotationMessage: normalizedRotationMessage,
  });
  if (codexTrigger) {
    return {
      kind: 'codex',
      trigger: codexTrigger,
      rotationMessage: normalizedRotationMessage,
    };
  }

  return { kind: 'none' };
}
