import { inferTeamProviderIdFromModel } from './teamProvider';

import type { TeamProviderId } from '@shared/types/team';

const ANTHROPIC_DEFAULT_CONTEXT_WINDOW = 200_000;
const ANTHROPIC_EXTENDED_CONTEXT_WINDOW = 1_000_000;
const OPENAI_COMPACT_CONTEXT_WINDOW = 200_000;
const OPENAI_DEFAULT_CONTEXT_WINDOW = 400_000;
const OPENAI_LONG_CONTEXT_WINDOW = 1_050_000;

export interface ContextUsageLike {
  input_tokens?: number;
  output_tokens?: number;
  cache_read_input_tokens?: number;
  cache_creation_input_tokens?: number;
  prompt_tokens?: number;
  completion_tokens?: number;
  total_tokens?: number;
  input_tokens_details?: {
    cached_tokens?: number;
  };
  prompt_tokens_details?: {
    cached_tokens?: number;
  };
  output_tokens_details?: {
    reasoning_tokens?: number;
  };
  completion_tokens_details?: {
    reasoning_tokens?: number;
  };
}

export type PromptInputSource =
  | 'anthropic_usage'
  | 'openai_responses_usage'
  | 'openai_chat_usage'
  | 'unavailable';

export interface DerivedContextMetrics {
  providerId: TeamProviderId | undefined;
  modelName: string | undefined;
  contextWindowTokens: number | null;
  promptInputTokens: number | null;
  outputTokens: number | null;
  contextUsedTokens: number | null;
  visibleContextTokens: number;
  promptInputSource: PromptInputSource;
  contextUsedSource: PromptInputSource | 'unavailable';
  promptInputPercentOfContextWindow: number | null;
  contextUsedPercentOfContextWindow: number | null;
  visibleContextPercentOfPromptInput: number | null;
}

interface InferContextWindowTokensParams {
  providerId?: TeamProviderId;
  modelName?: string;
  limitContext?: boolean;
}

interface DeriveContextMetricsParams extends InferContextWindowTokensParams {
  usage?: ContextUsageLike | null;
  contextWindowTokens?: number | null;
  visibleContextTokens?: number;
}

function readFiniteNumber(value: unknown): number | null {
  return typeof value === 'number' && Number.isFinite(value) ? value : null;
}

function readPositiveNumber(value: unknown): number | null {
  const num = readFiniteNumber(value);
  return num !== null && num > 0 ? num : null;
}

function computePercent(tokens: number | null, totalTokens: number | null): number | null {
  if (tokens === null || totalTokens === null || totalTokens <= 0) {
    return null;
  }
  if (!Number.isFinite(tokens) || tokens <= 0) {
    return 0;
  }
  return Math.min((tokens / totalTokens) * 100, 100);
}

function isOpenAiLongContextModel(modelName: string | undefined): boolean {
  const normalized = modelName?.trim().toLowerCase();
  if (!normalized) {
    return false;
  }

  return (
    normalized === 'gpt-5.4' ||
    normalized.startsWith('gpt-5.4-202') ||
    normalized === 'gpt-5.4-pro' ||
    normalized.startsWith('gpt-5.4-pro-202')
  );
}

function isOpenAiCompactContextModel(modelName: string | undefined): boolean {
  const normalized = modelName?.trim().toLowerCase();
  if (!normalized) {
    return false;
  }

  return normalized === 'codex-mini-latest' || normalized.startsWith('codex-mini-latest-');
}

function isAnthropicNativeLongContextModel(modelName: string | undefined): boolean {
  const normalized = modelName?.trim().toLowerCase();
  if (!normalized) {
    return false;
  }

  return (
    normalized.startsWith('claude-opus-4-7') ||
    normalized.startsWith('claude-opus-4-6') ||
    normalized.startsWith('claude-sonnet-4-6') ||
    normalized.startsWith('claude-mythos')
  );
}

function hasOpenAiPromptDetails(usage: ContextUsageLike): boolean {
  return (
    readFiniteNumber(usage.input_tokens_details?.cached_tokens) !== null ||
    readFiniteNumber(usage.prompt_tokens_details?.cached_tokens) !== null
  );
}

export function inferContextWindowTokens({
  providerId,
  modelName,
  limitContext,
}: InferContextWindowTokensParams): number | null {
  const resolvedProviderId = providerId ?? inferTeamProviderIdFromModel(modelName);
  const normalizedModel = modelName?.trim().toLowerCase();

  if (resolvedProviderId === 'anthropic') {
    if (limitContext) {
      return ANTHROPIC_DEFAULT_CONTEXT_WINDOW;
    }
    if (normalizedModel?.includes('[1m]') || isAnthropicNativeLongContextModel(normalizedModel)) {
      return ANTHROPIC_EXTENDED_CONTEXT_WINDOW;
    }
    return ANTHROPIC_DEFAULT_CONTEXT_WINDOW;
  }

  if (resolvedProviderId === 'codex') {
    if (isOpenAiCompactContextModel(normalizedModel)) {
      return OPENAI_COMPACT_CONTEXT_WINDOW;
    }
    return isOpenAiLongContextModel(normalizedModel)
      ? OPENAI_LONG_CONTEXT_WINDOW
      : OPENAI_DEFAULT_CONTEXT_WINDOW;
  }

  return null;
}

export function deriveContextMetrics({
  usage,
  providerId,
  modelName,
  contextWindowTokens,
  visibleContextTokens = 0,
  limitContext,
}: DeriveContextMetricsParams): DerivedContextMetrics {
  const resolvedProviderId = providerId ?? inferTeamProviderIdFromModel(modelName);
  const resolvedContextWindowTokens =
    readPositiveNumber(contextWindowTokens) ??
    inferContextWindowTokens({
      providerId: resolvedProviderId,
      modelName,
      limitContext,
    });
  const safeVisibleContextTokens =
    Number.isFinite(visibleContextTokens) && visibleContextTokens > 0 ? visibleContextTokens : 0;
  const safeUsage = usage ?? {};
  const outputTokens =
    readFiniteNumber(safeUsage.output_tokens) ?? readFiniteNumber(safeUsage.completion_tokens);
  const promptTokens = readFiniteNumber(safeUsage.prompt_tokens);
  const inputTokens = readFiniteNumber(safeUsage.input_tokens);
  const cacheReadTokens = readFiniteNumber(safeUsage.cache_read_input_tokens) ?? 0;
  const cacheCreationTokens = readFiniteNumber(safeUsage.cache_creation_input_tokens) ?? 0;

  let promptInputTokens: number | null = null;
  let promptInputSource: PromptInputSource = 'unavailable';

  if (promptTokens !== null) {
    promptInputTokens = promptTokens;
    promptInputSource = 'openai_chat_usage';
  } else if (inputTokens !== null) {
    const shouldUseAnthropicFormula =
      resolvedProviderId === 'anthropic' || cacheReadTokens > 0 || cacheCreationTokens > 0;

    if (shouldUseAnthropicFormula) {
      promptInputTokens = inputTokens + cacheReadTokens + cacheCreationTokens;
      promptInputSource = 'anthropic_usage';
    } else {
      const missingOpenAiPromptTelemetry =
        resolvedProviderId === 'codex' &&
        inputTokens === 0 &&
        cacheReadTokens === 0 &&
        cacheCreationTokens === 0 &&
        !hasOpenAiPromptDetails(safeUsage);

      if (!missingOpenAiPromptTelemetry) {
        promptInputTokens = inputTokens;
        promptInputSource = 'openai_responses_usage';
      }
    }
  }

  const contextUsedTokens =
    promptInputTokens !== null && outputTokens !== null ? promptInputTokens + outputTokens : null;

  return {
    providerId: resolvedProviderId,
    modelName,
    contextWindowTokens: resolvedContextWindowTokens,
    promptInputTokens,
    outputTokens,
    contextUsedTokens,
    visibleContextTokens: safeVisibleContextTokens,
    promptInputSource,
    contextUsedSource: contextUsedTokens !== null ? promptInputSource : 'unavailable',
    promptInputPercentOfContextWindow: computePercent(
      promptInputTokens,
      resolvedContextWindowTokens
    ),
    contextUsedPercentOfContextWindow: computePercent(
      contextUsedTokens,
      resolvedContextWindowTokens
    ),
    visibleContextPercentOfPromptInput: computePercent(safeVisibleContextTokens, promptInputTokens),
  };
}
