import * as z from 'zod/v4';

import type { DModelParameterId } from '~/common/stores/llms/llms.parameters';
import { LLM_IF_ANT_PromptCaching, LLM_IF_ANT_ToolsSearch, LLM_IF_HOTFIX_NoTemperature, LLM_IF_OAI_Chat, LLM_IF_OAI_Fn, LLM_IF_OAI_Reasoning, LLM_IF_OAI_Vision } from '~/common/stores/llms/llms.types';
import { Release } from '~/common/app.release';

import type { ModelDescriptionSchema, OrtVendorLookupResult } from '../llm.server.types';
import { createVariantInjector, ModelVariantMap } from '../llm.server.variants';
import { llmDevCheckModels_DEV } from '../models.mappings';


// Note: these model definitions are shared across Anthropic API, OpenRouter, and AWS Bedrock.
// Bedrock may support older/retired models longer than the Anthropic API, so we keep
// retired models in this file rather than removing them immediately. Removal is manual.

// configuration
const DEV_DEBUG_ANTHROPIC_MODELS = (Release.TenantSlug as any) === 'staging' /* ALSO IN STAGING! */ || Release.IsNodeDevBuild;

// Cap applied ONLY to models that declare the `llmVndAnt1MContext` opt-in parameter
// (Sonnet 4.5 / Sonnet 4 - beta 1M with tiered pricing, retiring 2026-04-30).
// For models with 1M GA at standard pricing (Opus 4.7/4.6, Sonnet 4.6 since 2026-03-13),
// we report the API-provided context window as-is (1M) and do NOT add the opt-in.
// Set to `false` to disable the cap entirely (useful for debugging mismatches).
const ANT_CAP_CONTEXT_WINDOW: number | false = 200_000;

/** True if the model uses the opt-in `llmVndAnt1MContext` beta path (Sonnet 4.5 / Sonnet 4). */
function _hasLegacy1MContextOptIn(model: Pick<ModelDescriptionSchema, 'parameterSpecs'>): boolean {
  return !!model.parameterSpecs?.some(s => s.paramId === 'llmVndAnt1MContext');
}



const IF_4 = [LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_ANT_PromptCaching];
const IF_4_R = [...IF_4, LLM_IF_OAI_Reasoning];
// 4.7+: temperature/top_p/top_k return 400; HOTFIX strips temperature client-side (top_p handled in dispatch)
const IF_47 = [...IF_4, LLM_IF_HOTFIX_NoTemperature];
const IF_47_R = [...IF_4_R, LLM_IF_HOTFIX_NoTemperature];


// Anthropic Parameters Semantics:
// - llmVndAntEffort             Anthropic effort: each model declares its subset via enumValues. 4.7 adds `xhigh`.
// - llmVndAnt1MContext         required for Sonnet 4.5 / Sonnet 4 (beta, tiered pricing, retiring 2026-04-30).
//                              Not needed for Opus 4.7/4.6 and Sonnet 4.6 (1M GA at standard pricing since 2026-03-13).
// - llmVndAntSkills            2026-02-06: seems GA to any model now: a parameter spec for user/UI configurability
// - llmVndAntThinkingBudget    2026-02-06: deprecated since 4.6 in favor of adaptive thinking; 4.7 REMOVES manual budgets
//                              entirely (adaptive-only). We keep the param (hidden, initialValue -1) as our "force adaptive"
//                              sentinel on 4.6/4.7 thinking variants, and for manual budget control on 4.5/earlier.
// - llmVndAntWebFetch/Search   seem an API feature available on all models

const ANT_TOOLS: Exclude<ModelDescriptionSchema['parameterSpecs'], undefined> = [
  { paramId: 'llmVndAntSkills' },
  { paramId: 'llmVndAntWebFetch' },
  { paramId: 'llmVndAntWebFetchMaxUses' },
  { paramId: 'llmVndAntWebSearch' },
  { paramId: 'llmVndAntWebSearchMaxUses' },
] as const;

/** Dynamic filtering for web search/fetch - only Opus/Sonnet 4.6+ */
const ANT_TOOLS_DYNAMIC: Exclude<ModelDescriptionSchema['parameterSpecs'], undefined> = [
  ...ANT_TOOLS,
  { paramId: 'llmVndAntWebDynamic' },
] as const;


const _hardcodedAnthropicThinkingVariants: ModelVariantMap & { [id: string]: { idVariant: 'thinking' /* this is here because of OpenRouter matching, see below - all these are assued as thinking variants */ } } = {

  // NOTE: what's not redefined below is inherited from the underlying model definition

  // Claude 4.7 models with thinking variants (adaptive-only, manual budgets removed)
  'claude-opus-4-7': {
    idVariant: 'thinking',
    label: 'Claude Opus 4.7 (Adaptive)',
    description: 'Claude Opus 4.7 with adaptive thinking for the most complex reasoning and agentic coding',
    interfaces: [...IF_47_R, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget', hidden: true, initialValue: -1 /* FORCE adaptive - 4.7 rejects budget_tokens */ },
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'xhigh', 'max'] },
      ...ANT_TOOLS_DYNAMIC,
    ],
    benchmark: { cbaElo: 1504 }, // claude-opus-4-7-thinking
  },

  // Claude 4.6 models with thinking variants
  'claude-opus-4-6': {
    idVariant: 'thinking',
    label: 'Claude Opus 4.6 (Adaptive)',
    description: 'Claude Opus 4.6 with adaptive thinking mode for the most complex reasoning and agentic workflows',
    interfaces: [...IF_4_R, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget', hidden: true, initialValue: -1 /* FORCE adaptive */ },
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'max'] },
      { paramId: 'llmVndAntInfSpeed' },
      ...ANT_TOOLS_DYNAMIC,
    ],
    benchmark: { cbaElo: 1502 }, // claude-opus-4-6-thinking
  },

  'claude-sonnet-4-6': {
    idVariant: 'thinking',
    label: 'Claude Sonnet 4.6 (Adaptive)',
    description: 'Claude Sonnet 4.6 with adaptive thinking mode for balanced speed and intelligence',
    interfaces: [...IF_4_R, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget', hidden: true, initialValue: -1 /* FORCE adaptive */ },
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'max'] },
      ...ANT_TOOLS_DYNAMIC,
    ],
    benchmark: { cbaElo: 1463 + 1 }, // 1 (thinking) + claude-sonnet-4-6
  },

  // Claude 4.5 models with thinking variants
  'claude-opus-4-5-20251101': {
    idVariant: 'thinking',
    label: 'Claude Opus 4.5 (Thinking)',
    description: 'Claude Opus 4.5 with extended thinking mode for complex reasoning and agentic workflows',
    interfaces: [...IF_4_R, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high'] },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1473 }, // claude-opus-4-5-20251101-thinking-32k
    maxCompletionTokens: 32000,
  },

  'claude-sonnet-4-5-20250929': {
    idVariant: 'thinking',
    label: 'Claude Sonnet 4.5 (Thinking)',
    description: 'Claude Sonnet 4.5 with extended thinking mode enabled for complex reasoning',
    maxCompletionTokens: 64000,
    interfaces: [...IF_4_R, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      { paramId: 'llmVndAnt1MContext' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1452 }, // claude-sonnet-4-5-20250929-thinking-32k
  },

  'claude-haiku-4-5-20251001': {
    idVariant: 'thinking',
    label: 'Claude Haiku 4.5 (Thinking)',
    description: 'Claude Haiku 4.5 with extended thinking mode - first Haiku model with reasoning capabilities',
    maxCompletionTokens: 64000,
    interfaces: IF_4_R,
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1408 + 1 }, // 1 (thinking) + claude-haiku-4-5-20251001
  },

  // Claude 4.1 models with thinking variants
  'claude-opus-4-1-20250805': {
    idVariant: 'thinking',
    label: 'Claude Opus 4.1 (Thinking)',
    description: 'Claude Opus 4.1 with extended thinking mode enabled for complex reasoning',
    maxCompletionTokens: 32000,
    interfaces: IF_4_R,
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1449 }, // claude-opus-4-1-20250805-thinking-16k
  },

  // Claude 4 models with thinking variants
  'claude-opus-4-20250514': {
    idVariant: 'thinking',
    hidden: true, // superseded by 4.1
    label: 'Claude Opus 4 (Thinking)',
    description: 'Claude Opus 4 with extended thinking mode enabled for complex reasoning',
    maxCompletionTokens: 32000,
    interfaces: IF_4_R,
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1424 }, // claude-opus-4-20250514-thinking-16k
  },

  'claude-sonnet-4-20250514': {
    idVariant: 'thinking',
    label: 'Claude Sonnet 4 (Thinking)',
    description: 'Claude Sonnet 4 with extended thinking mode enabled for complex reasoning',
    maxCompletionTokens: 64000,
    interfaces: IF_4_R,
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      { paramId: 'llmVndAnt1MContext' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1399 }, // claude-sonnet-4-20250514-thinking-32k
  },

  // Changes to the thinking variant (same model ID) for the Claude Sonnet 3.7 model
  'claude-3-7-sonnet-20250219': {
    idVariant: 'thinking',
    label: 'Claude Sonnet 3.7 (Thinking)',
    description: 'Claude 3.7 with extended thinking mode enabled for complex reasoning',
    maxCompletionTokens: 64000,
    interfaces: IF_4_R,
    parameterSpecs: [
      { paramId: 'llmVndAntThinkingBudget' },
      ...ANT_TOOLS,
    ],
    benchmark: { cbaElo: 1387 }, // claude-3-7-sonnet-20250219-thinking-32k
  },

} as const;

export function llmsAntInjectVariants(acc: ModelDescriptionSchema[], model: ModelDescriptionSchema): ModelDescriptionSchema[] {
  return createVariantInjector(_hardcodedAnthropicThinkingVariants, 'before')(acc, model);
}


export const hardcodedAnthropicModels: (ModelDescriptionSchema & { isLegacy?: boolean })[] = [

  // Claude 4.7 models
  {
    id: 'claude-opus-4-7', // Active - 2026-04-16
    label: 'Claude Opus 4.7',
    description: 'Most capable generally available model for complex reasoning and agentic coding',
    contextWindow: 1_000_000, // 1M GA at standard pricing (no opt-in required)
    maxCompletionTokens: 128000,
    interfaces: [...IF_47, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'xhigh', 'max'] },
      ...ANT_TOOLS_DYNAMIC,
    ],
    // Opus 4.7: flat $5/$25 pricing across entire 1M context window (no long-context premium, no fast mode)
    // Breaking changes vs 4.6: extended thinking budgets removed (adaptive-only), temperature/top_p/top_k rejected,
    // thinking content omitted by default, new tokenizer (~1x to 1.35x tokens for same text), no prefill.
    chatPrice: { input: 5, output: 25, cache: { cType: 'ant-bp', read: 0.50, write: 6.25, duration: 300 } },
    benchmark: { cbaElo: 1497 }, // claude-opus-4-7
  },

  // Claude 4.6 models
  {
    id: 'claude-opus-4-6', // Active
    label: 'Claude Opus 4.6',
    description: 'Previous most intelligent model for complex agents and coding, with adaptive thinking',
    contextWindow: 1_000_000, // 1M GA at standard pricing since 2026-03-13 (no opt-in required)
    maxCompletionTokens: 128000,
    interfaces: [...IF_4, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'max'] },
      { paramId: 'llmVndAntInfSpeed' },
      ...ANT_TOOLS_DYNAMIC,
    ],
    // Opus 4.6: flat $5/$25 pricing (1M context GA at standard pricing since 2026-03-13, no opt-in required)
    chatPrice: { input: 5, output: 25, cache: { cType: 'ant-bp', read: 0.50, write: 6.25, duration: 300 } },
    benchmark: { cbaElo: 1496 }, // claude-opus-4-6
  },
  {
    id: 'claude-sonnet-4-6', // Active
    label: 'Claude Sonnet 4.6',
    description: 'Best combination of speed and intelligence for everyday tasks',
    contextWindow: 1_000_000, // 1M GA at standard pricing since 2026-03-13 (no opt-in required)
    maxCompletionTokens: 128000, // docs say 64000, API reports 128000
    interfaces: [...IF_4, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high', 'max'] },
      ...ANT_TOOLS_DYNAMIC,
    ],
    // Sonnet 4.6: flat $3/$15 pricing (1M context GA at standard pricing since 2026-03-13, no opt-in required)
    chatPrice: { input: 3, output: 15, cache: { cType: 'ant-bp', read: 0.30, write: 3.75, duration: 300 } },
    benchmark: { cbaElo: 1463 }, // claude-sonnet-4-6
  },

  // Claude 4.5 models
  {
    id: 'claude-opus-4-5-20251101', // Active
    label: 'Claude Opus 4.5',
    description: 'Previous most intelligent model with advanced reasoning for complex agentic workflows',
    contextWindow: 200000,
    maxCompletionTokens: 64000,
    interfaces: [...IF_4, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAntEffort', enumValues: ['low', 'medium', 'high'] },
      ...ANT_TOOLS,
    ],
    chatPrice: { input: 5, output: 25, cache: { cType: 'ant-bp', read: 0.50, write: 6.25, duration: 300 } },
    benchmark: { cbaElo: 1469 }, // claude-opus-4-5-20251101
  },
  {
    id: 'claude-sonnet-4-5-20250929', // Active
    label: 'Claude Sonnet 4.5',
    description: 'Previous best combination of speed and intelligence for complex agents and coding',
    contextWindow: 200000,
    maxCompletionTokens: 64000,
    interfaces: [...IF_4, LLM_IF_ANT_ToolsSearch],
    parameterSpecs: [
      { paramId: 'llmVndAnt1MContext' }, // 1M context beta retires 2026-04-30 for this model
      ...ANT_TOOLS,
    ],
    // Note: Tiered pricing - ≤200K: $3/$15, >200K: $6/$22.50 (with 1M context enabled)
    // Cache pricing also tiered: write 1.25× input, read 0.10× input
    chatPrice: {
      input: [{ upTo: 200000, price: 3 }, { upTo: null, price: 6 }],
      output: [{ upTo: 200000, price: 15 }, { upTo: null, price: 22.50 }],
      cache: {
        cType: 'ant-bp',
        read: [{ upTo: 200000, price: 0.30 }, { upTo: null, price: 0.60 }],
        write: [{ upTo: 200000, price: 3.75 }, { upTo: null, price: 7.50 }],
        duration: 300,
      },
    },
    benchmark: { cbaElo: 1452 }, // claude-sonnet-4-5-20250929
  },
  {
    id: 'claude-haiku-4-5-20251001', // Active
    label: 'Claude Haiku 4.5',
    description: 'Fastest model with exceptional speed and performance',
    contextWindow: 200000,
    maxCompletionTokens: 64000,
    interfaces: IF_4,
    parameterSpecs: ANT_TOOLS,
    chatPrice: { input: 1, output: 5, cache: { cType: 'ant-bp', read: 0.10, write: 1.25, duration: 300 } },
    benchmark: { cbaElo: 1408 }, // claude-haiku-4-5-20251001
  },

  // Claude 4.1 models
  {
    id: 'claude-opus-4-1-20250805', // Active
    label: 'Claude Opus 4.1',
    description: 'Exceptional model for specialized complex tasks requiring advanced reasoning',
    contextWindow: 200000,
    maxCompletionTokens: 32000,
    interfaces: IF_4,
    parameterSpecs: ANT_TOOLS,
    chatPrice: { input: 15, output: 75, cache: { cType: 'ant-bp', read: 1.50, write: 18.75, duration: 300 } },
    benchmark: { cbaElo: 1447 }, // claude-opus-4-1-20250805
  },

  // Claude 4 models
  {
    hidden: true, // Deprecated: April 14, 2026 | Retiring: June 15, 2026 | Replacement: claude-opus-4-7
    id: 'claude-opus-4-20250514', // Deprecated
    label: 'Claude Opus 4 [Deprecated]',
    description: 'Previous flagship model. Deprecated April 14, 2026, retiring June 15, 2026.',
    contextWindow: 200000,
    maxCompletionTokens: 32000,
    interfaces: IF_4,
    parameterSpecs: ANT_TOOLS,
    chatPrice: { input: 15, output: 75, cache: { cType: 'ant-bp', read: 1.50, write: 18.75, duration: 300 } },
    benchmark: { cbaElo: 1412 }, // claude-opus-4-20250514
    isLegacy: true,
  },
  {
    hidden: true, // Deprecated: April 14, 2026 | Retiring: June 15, 2026 | Replacement: claude-sonnet-4-6
    id: 'claude-sonnet-4-20250514', // Deprecated
    label: 'Claude Sonnet 4 [Deprecated]',
    description: 'High-performance model. Deprecated April 14, 2026, retiring June 15, 2026.',
    contextWindow: 200000,
    maxCompletionTokens: 64000,
    interfaces: IF_4,
    parameterSpecs: [
      { paramId: 'llmVndAnt1MContext' }, // 1M context beta retires 2026-04-30 for this model
      ...ANT_TOOLS,
    ],
    // Note: Tiered pricing - ≤200K: $3/$15, >200K: $6/$22.50 (with 1M context enabled)
    // Cache pricing also tiered: write 1.25× input, read 0.10× input
    chatPrice: {
      input: [{ upTo: 200000, price: 3 }, { upTo: null, price: 6 }],
      output: [{ upTo: 200000, price: 15 }, { upTo: null, price: 22.50 }],
      cache: {
        cType: 'ant-bp',
        read: [{ upTo: 200000, price: 0.30 }, { upTo: null, price: 0.60 }],
        write: [{ upTo: 200000, price: 3.75 }, { upTo: null, price: 7.50 }],
        duration: 300,
      },
    },
    benchmark: { cbaElo: 1389 }, // claude-sonnet-4-20250514
    isLegacy: true,
  },

  // Claude 3.7 models
  {
    id: 'claude-3-7-sonnet-20250219', // Retired | Deprecated: October 28, 2025 | Retired: February 19, 2026 | Replacement: claude-opus-4-6
    label: 'Claude Sonnet 3.7 [Retired]',
    description: 'High-performance model with early extended thinking. Retired February 19, 2026.',
    contextWindow: 200000,
    maxCompletionTokens: 64000,
    interfaces: IF_4,
    parameterSpecs: ANT_TOOLS,
    chatPrice: { input: 3, output: 15, cache: { cType: 'ant-bp', read: 0.30, write: 3.75, duration: 300 } },
    benchmark: { cbaElo: 1371 }, // claude-3-7-sonnet-20250219
    hidden: true, // retired
    isLegacy: true,
  },

  // Claude 3.5 models
  // retired: 'claude-3-5-sonnet-20241022'
  // retired: 'claude-3-5-sonnet-20240620'
  {
    id: 'claude-3-5-haiku-20241022', // Retired | Deprecated: December 19, 2025 | Retired: February 19, 2026
    label: 'Claude Haiku 3.5 [Retired]',
    description: 'Intelligence at blazing speeds. Retired February 19, 2026.',
    contextWindow: 200000,
    maxCompletionTokens: 8192,
    interfaces: IF_4,
    parameterSpecs: ANT_TOOLS,
    chatPrice: { input: 0.80, output: 4.00, cache: { cType: 'ant-bp', read: 0.08, write: 1.00, duration: 300 } },
    benchmark: { cbaElo: 1323 }, // claude-3-5-haiku-20241022
    hidden: true, // retired
    isLegacy: true,
  },

  // Claude 3 models
  // retired: 'claude-3-opus-20240229' - Retired January 5, 2026
  {
    hidden: true, // deprecated
    id: 'claude-3-haiku-20240307', // Deprecated | Deprecated: February 19, 2026 | Retiring: April 20, 2026 | Replacement: claude-haiku-4-5-20251001
    label: 'Claude Haiku 3 [Deprecated]',
    description: 'Fast and compact model for near-instant responsiveness. Deprecated February 19, 2026, retiring April 20, 2026.',
    contextWindow: 200000,
    maxCompletionTokens: 4096,
    interfaces: IF_4,
    chatPrice: { input: 0.25, output: 1.25, cache: { cType: 'ant-bp', read: 0.03, write: 0.30, duration: 300 } },
    benchmark: { cbaElo: 1260 }, // claude-3-haiku-20240307
    isLegacy: true,
  },

  // Legacy/Retired models
  // retired: 'claude-3-sonnet-20240229'
  // retired: 'claude-2.1'
  // retired: 'claude-2.0'
];


// -- Wire Types --

/**
 * Namespace for the Anthropic API Models List response schema.
 * NOTE: not merged into AIX because of possible circular dependency issues - future work.
 *
 * As of 2026-04: the /v1/models API returns expanded model metadata including
 * max_input_tokens, max_tokens, and a capabilities object with detailed support
 * flags for effort levels, thinking types, image/pdf input, code execution, etc.
 */
export namespace AnthropicWire_API_Models_List {

  const _Supported_schema = z.object({
    supported: z.boolean(),
  });

  const _Capabilities_schema = z.object({
    batch: _Supported_schema.nullish(),
    citations: _Supported_schema.nullish(),
    code_execution: _Supported_schema.nullish(),
    context_management: z.object({
      supported: z.boolean(),
      clear_thinking_20251015: _Supported_schema.nullish(),
      clear_tool_uses_20250919: _Supported_schema.nullish(),
      compact_20260112: _Supported_schema.nullish(),
    }).nullish(),
    effort: z.object({
      supported: z.boolean(),
      low: _Supported_schema.nullish(),
      medium: _Supported_schema.nullish(),
      high: _Supported_schema.nullish(),
      max: _Supported_schema.nullish(),
    }).nullish(),
    image_input: _Supported_schema.nullish(),
    pdf_input: _Supported_schema.nullish(),
    structured_outputs: _Supported_schema.nullish(),
    thinking: z.object({
      supported: z.boolean(),
      types: z.object({
        enabled: _Supported_schema.nullish(),
        adaptive: _Supported_schema.nullish(),
      }).nullish(),
    }).nullish(),
  });

  export type ModelObject = z.infer<typeof ModelObject_schema>;
  const ModelObject_schema = z.object({
    type: z.literal('model'),
    id: z.string(),
    display_name: z.string(),
    created_at: z.string(),
    // expanded fields (2026-04) - per API docs: `number | null`
    max_input_tokens: z.number().nullish(),
    max_tokens: z.number().nullish(),
    capabilities: _Capabilities_schema.nullish(),
  });

  export const Response_schema = z.object({
    data: z.array(ModelObject_schema),
    has_more: z.boolean(),
    first_id: z.string().nullable(),
    last_id: z.string().nullable(),
  });

}


// -- Helper Functions (DEV) --

export function llmsAntValidateModelDefs_DEV(availableModels: AnthropicWire_API_Models_List.ModelObject[]): void {
  if (DEV_DEBUG_ANTHROPIC_MODELS) {
    llmDevCheckModels_DEV('Anthropic', availableModels.map(m => m.id), hardcodedAnthropicModels.map(m => m.id));
    _llmsAntCheckApiCapabilities_DEV(availableModels);
  }
}

/**
 * DEV: Compares API-provided values against hardcoded definitions:
 * - maxCompletionTokens, contextWindow, and effort enumValues.
 */
function _llmsAntCheckApiCapabilities_DEV(availableModels: AnthropicWire_API_Models_List.ModelObject[]): void {
  for (const apiModel of availableModels) {
    // find hardcoded known model
    const hc = hardcodedAnthropicModels.find(m => m.id === apiModel.id);
    if (!hc) {
      // console.log(`[DEV] Anthropic '${apiModel.id}': no hardcoded definition for this model - consider adding one for pricing, benchmarks, and interface/parameter specs (except token limits which are API-authoritative)`); // also check if it's a known thinking variant without its own hardcoded def
      continue;
    }

    // maxCompletionTokens mismatch
    if (apiModel.max_tokens && hc.maxCompletionTokens && apiModel.max_tokens !== hc.maxCompletionTokens)
      console.log(`[DEV] Anthropic '${apiModel.id}': maxCompletionTokens mismatch - hardcoded=${hc.maxCompletionTokens}, api=${apiModel.max_tokens}`);

    // contextWindow vs max_input_tokens (only cap if the model uses the opt-in beta path)
    if (apiModel.max_input_tokens) {
      const shouldCap = _hasLegacy1MContextOptIn(hc) && !!ANT_CAP_CONTEXT_WINDOW;
      const expectedBase = shouldCap ? Math.min(apiModel.max_input_tokens, ANT_CAP_CONTEXT_WINDOW) : apiModel.max_input_tokens;
      if (hc.contextWindow !== null && hc.contextWindow !== expectedBase)
        console.log(`[DEV] Anthropic '${apiModel.id}': contextWindow mismatch - hardcoded=${hc.contextWindow}, api=${apiModel.max_input_tokens}${shouldCap ? ` (capped=${expectedBase})` : ''}`);
    }

    // effort enumValues mismatch
    if (apiModel.capabilities?.effort?.supported) {
      const apiEffort = (['low', 'medium', 'high', 'max'] as const).filter(l => apiModel.capabilities?.effort?.[l]?.supported);
      const knownEffortSpec = hc.parameterSpecs?.find(s => s.paramId === 'llmVndAntEffort');
      if (knownEffortSpec?.enumValues) {
        const hardcoded = knownEffortSpec.enumValues.join(',');
        const fromApi = apiEffort.join(',');
        if (hardcoded !== fromApi)
          console.log(`[DEV] Anthropic '${apiModel.id}': effort enumValues mismatch - hardcoded=[${hardcoded}], api=[${fromApi}]`);
      }
    }
  }
}


// -- Helper Functions --

/**
 * Create a placeholder ModelDescriptionSchema for Anthropic models not in the hardcoded list.
 * Uses API-provided capabilities for accurate day-0 support when available.
 */
export function llmsAntCreatePlaceholderModel(model: AnthropicWire_API_Models_List.ModelObject): ModelDescriptionSchema {
  const caps = model.capabilities;

  // Interfaces

  const interfaces: ModelDescriptionSchema['interfaces'] = [
    LLM_IF_OAI_Chat,
    LLM_IF_OAI_Fn,
    LLM_IF_ANT_PromptCaching,
  ];
  if (!caps || caps?.image_input?.supported) interfaces.push(LLM_IF_OAI_Vision);
  if (!caps || caps?.thinking?.supported) interfaces.push(LLM_IF_OAI_Reasoning);

  // ParameterSpecs

  // derive effort enumValues
  const parameterSpecs: ModelDescriptionSchema['parameterSpecs'] = [];
  if (caps?.effort?.supported) {
    const effortValues: string[] = [];
    if (caps.effort.low?.supported) effortValues.push('low');
    if (caps.effort.medium?.supported) effortValues.push('medium');
    if (caps.effort.high?.supported) effortValues.push('high');
    if (caps.effort.max?.supported) effortValues.push('max');
    if (effortValues.length)
      parameterSpecs.push({ paramId: 'llmVndAntEffort', enumValues: effortValues });
  }

  // derive thinking params
  if (caps?.thinking?.supported) {
    // Adaptive thinking (4.6+) - force adaptive mode
    if (caps.thinking.types?.adaptive?.supported)
      parameterSpecs.push({ paramId: 'llmVndAntThinkingBudget', hidden: true, initialValue: -1 });
    // Classic extended thinking
    else if (caps.thinking.types?.enabled?.supported)
      parameterSpecs.push({ paramId: 'llmVndAntThinkingBudget' });
  }

  // +standard tool params
  // Note: 1M context is GA for recent Anthropic models - we do NOT add the `llmVndAnt1MContext` opt-in
  // for unknown models. If a 0-day model still requires the beta header (rare now), users can hit it
  // through a hardcoded definition instead.
  parameterSpecs.push(...ANT_TOOLS);

  const maxInputTokens = model.max_input_tokens;
  return {
    id: model.id,
    idVariant: '::placeholder',
    label: model.display_name,
    created: Math.round(new Date(model.created_at).getTime() / 1000),
    description: 'Newest model, description not available yet.',
    contextWindow: maxInputTokens ?? 200_000, // report API value as-is (no cap for unknown models)
    maxCompletionTokens: model.max_tokens || 32768,
    interfaces,
    parameterSpecs,
    // chatPrice: ...
    // benchmark: ...
  };
}

/**
 * Apply API-provided metadata to a known hardcoded model definition.
 * API data is authoritative for token limits; hardcoded data is kept for
 * pricing, benchmarks, interfaces, and parameter specs (except effort enumValues).
 */
export function llmsAntFuseModelKnowledge(knownModel: ModelDescriptionSchema, apiModel: AnthropicWire_API_Models_List.ModelObject): ModelDescriptionSchema {
  let updated = knownModel;

  // apply creation time if not set
  if (!updated.created && apiModel.created_at)
    updated = { ...updated, created: Math.round(new Date(apiModel.created_at).getTime() / 1000) };

  // API-authoritative: context window
  // Only cap when the model uses the `llmVndAnt1MContext` opt-in (beta 1M path, e.g. Sonnet 4.5 / 4).
  // For models with 1M GA at standard pricing (no opt-in), we report the API value as-is.
  if (apiModel.max_input_tokens) {
    const shouldCap = _hasLegacy1MContextOptIn(updated) && !!ANT_CAP_CONTEXT_WINDOW;
    const apiContextWindow = shouldCap ? Math.min(apiModel.max_input_tokens, ANT_CAP_CONTEXT_WINDOW) : apiModel.max_input_tokens;
    if (updated.contextWindow !== apiContextWindow)
      updated = { ...updated, contextWindow: apiContextWindow };
  }

  // API-authoritative: max output tokens
  if (apiModel.max_tokens && apiModel.max_tokens !== updated.maxCompletionTokens)
    updated = { ...updated, maxCompletionTokens: apiModel.max_tokens };

  // DISABLED for now - not sure this helps much, as we editoralize this:
  // API-authoritative: effort enumValues (fixes stale hardcoded values)
  // const caps = apiModel.capabilities;
  // if (caps?.effort?.supported && updated.parameterSpecs) {
  //   const apiEffortValues = (['low', 'medium', 'high', 'max'] as const).filter(l => caps.effort?.[l]?.supported);
  //   if (apiEffortValues.length) {
  //     const effortIdx = updated.parameterSpecs.findIndex(s => s.paramId === 'llmVndAntEffort');
  //     if (effortIdx >= 0) {
  //       const currentValues = updated.parameterSpecs[effortIdx].enumValues;
  //       if (currentValues && currentValues.join(',') !== apiEffortValues.join(',')) {
  //         const newSpecs = [...updated.parameterSpecs];
  //         newSpecs[effortIdx] = { ...newSpecs[effortIdx], enumValues: apiEffortValues };
  //         updated = { ...updated, parameterSpecs: newSpecs };
  //       }
  //     }
  //   }
  // }

  return updated;
}

// -- Anthropic-through-Bedrock models lookup --

/** Find a hardcoded Anthropic model definition by its Bedrock model ID. */
export function llmBedrockFindAnthropicModel(bedrockBaseId: string): (ModelDescriptionSchema & { isLegacy?: boolean }) | undefined {
  const anthropicId = _llmBedrockToAnthropicModelId(bedrockBaseId);
  if (!anthropicId) return undefined;
  return hardcodedAnthropicModels.find(m => m.id === anthropicId);
}

function _llmBedrockToAnthropicModelId(bedrockBaseId: string): string | undefined {
  if (!bedrockBaseId.startsWith('anthropic.')) return undefined;
  // e.g. anthropic.claude-opus-4-6-v1 -> claude-opus-4-6; anthropic.claude-opus-4-5-20251101-v1:0   -> claude-opus-4-5-20251101
  return bedrockBaseId.slice('anthropic.'.length).replace(/-v\d+(:\d+)?$/, '');
}

// Bedrock supports these interfaces (no Anthropic web tools)
const _BEDROCK_ANT_IF_ALLOWLIST: ReadonlySet<string> = new Set([
  LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Reasoning,
  LLM_IF_ANT_PromptCaching,
] as const);

// NOTE: llmVndAntInfSpeed not available on Bedrock, llmVndAntWebFetch/llmVndAntSkills not available
const _BEDROCK_ANT_PARAM_ALLOWLIST: ReadonlySet<string> = new Set([
  // bedrock params to not strip
  'llmVndBedrockAPI',
  // supported
  'llmVndAnt1MContext',
  'llmVndAntEffort',
  'llmVndAntThinkingBudget',
  // Not supported by Bedrock
  // 'llmVndAntInfSpeed', // Bad Request - speed: Extra inputs are not permitted
  // 'llmVndAntSkills', // code execution is not supported: https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#platform-availability
  // 'llmVndAntWebFetch', // Bad Request - tools.0: Input tag 'web_fetch_20250910' found using 'type' does not match any of the expected tags: 'bash_20250124', 'custom', 'text_editor_20250124', 'text_editor_20250429', 'text_editor_20250728', 'web_search_20250305'
  // 'llmVndAntWebFetchMaxUses', // requires llmVndAntWebFetch to be supported
  // 'llmVndAntWebSearch', // Bedrock should support web search, but we get 'Bad Request' if the 'web_search_20250305' tool is added
  // 'llmVndAntWebSearchMaxUses', // requires llmVndAntWebSearch to be supported
] as const satisfies DModelParameterId[]);

/** Strip unsupported interfaces and params from an Anthropic model for Bedrock */
export function llmBedrockStripAnthropicMDS(model: ModelDescriptionSchema): ModelDescriptionSchema {
  if (!model.parameterSpecs && !model.interfaces.some(i => !_BEDROCK_ANT_IF_ALLOWLIST.has(i)))
    return model; // nothing to filter
  return {
    ...model,
    interfaces: model.interfaces.filter(i => _BEDROCK_ANT_IF_ALLOWLIST.has(i)),
    ...(model.parameterSpecs ? {
      parameterSpecs: model.parameterSpecs.filter(spec => _BEDROCK_ANT_PARAM_ALLOWLIST.has(spec.paramId)),
    } : {}),
  };
}


// -- Anthropic-through-OpenRouter Vendor Lookup --

const _ORT_ANT_IF_ALLOWLIST: ReadonlySet<string> = new Set([
  LLM_IF_OAI_Chat, LLM_IF_OAI_Vision, LLM_IF_OAI_Fn, LLM_IF_OAI_Reasoning,
] as const);
// NOTE: llmVndAntInfSpeed intentionally NOT included - fast mode not available through OpenRouter
const _ORT_ANT_PARAM_ALLOWLIST: ReadonlySet<string> = new Set([
  'llmVndAntEffort', // Anthropic effort
  'llmVndAntThinkingBudget',
] as const satisfies DModelParameterId[]);

/**
 * Lookup for OpenRouter: match an OR Anthropic model ID to a known hardcoded model
 * @param orModelName - The model name after stripping 'anthropic/' prefix (e.g. 'claude-4.6-opus')
 */
export function llmOrtAntLookup_ThinkingVariants(orModelName: string): OrtVendorLookupResult | undefined {

  // tokenize the OR name into a set of tokens ['claude', '3', '7', 'sonnet'], ignoring order, dots vs dashes, date suffixes, and OR-specific tags (e.g. ':beta')
  const orTokens = new Set(orModelName.replace(/:.*$/, '').replace(/\./g, '-').replace(/-\d{8}$/, '').split('-'));

  // find a known model by matching all tokens
  const _knownModel = hardcodedAnthropicModels.find((m) => {
    // tokenize known model name, removing the '...-date' suffix
    const antTokens = new Set(m.id.replace(/-\d{8}$/, '').split('-'));
    return antTokens.size === orTokens.size && [...antTokens].every((t) => orTokens.has(t));
  });
  if (!_knownModel) return undefined;

  // found a model
  let model = _knownModel;

  // if there's a variant, it must be the thinking variant, so return that
  const thinkingVariant = _hardcodedAnthropicThinkingVariants[model.id];
  if (thinkingVariant && !Array.isArray(thinkingVariant)) {
    const { idVariant: _idV, variantOrder: _vo, ...variantChanges } = thinkingVariant;
    model = { ...model, ...variantChanges };
  }

  // allowlists on interfaces and parameter specs
  const interfaces = model.interfaces.filter((i) => _ORT_ANT_IF_ALLOWLIST.has(i));

  const parameterSpecs = model.parameterSpecs
    ?.filter((spec) => _ORT_ANT_PARAM_ALLOWLIST.has(spec.paramId))
    .map((spec) => ({ ...spec }));

  // initialTemperature: not set - Anthropic models use the global fallback (0.5)
  return { interfaces, parameterSpecs };
}
