import { describe, expect, it } from "vitest";
import {
  createModelProvider,
  getProviderCapabilities,
  resolveProviderPromptCacheDiagnostics,
  resolveProviderOptions,
  resolveProviderTimeoutMs,
} from "./model-provider.js";
import { resolveProviderDecisionTransport } from "./provider-metadata.js";

describe("model provider decision parsing", () => {
  it("resolves decision transports for the supported provider families", () => {
    expect(
      getProviderCapabilities({
        kind: "openai",
        model: "gpt-5-mini",
      }).supportedDecisionTransports,
    ).toEqual(expect.arrayContaining(["native_tools"]));
    expect(
      getProviderCapabilities({
        kind: "anthropic",
        model: "claude-sonnet-4",
      }).supportedDecisionTransports,
    ).toEqual(expect.arrayContaining(["native_tools"]));
    expect(
      getProviderCapabilities({
        kind: "google",
        model: "gemini-2.5-pro",
      }).supportedDecisionTransports,
    ).toEqual(expect.arrayContaining(["native_tools"]));
    expect(
      getProviderCapabilities({
        baseURL: "https://example.test/v1",
        compatibilityMode: "compatible",
        kind: "openai",
        model: "glm-5",
      }).defaultDecisionTransport,
    ).toBe("native_tools");
  });

  it("normalizes auto decision transport to native tools", () => {
    expect(
      resolveProviderDecisionTransport({
        decisionTransport: "auto",
        kind: "openai",
        model: "gpt-5-mini",
      }),
    ).toBe("native_tools");
  });

  it("preserves explicit openai compatibility modes", () => {
    expect(
      createModelProvider({
        compatibilityMode: "compatible",
        kind: "openai",
        model: "gpt-5-mini",
      }).config.compatibilityMode,
    ).toBe("compatible");

    expect(
      createModelProvider({
        compatibilityMode: "native",
        kind: "openai",
        model: "gpt-5-mini",
        baseURL: "https://example.test/v1",
      }).config.compatibilityMode,
    ).toBe("native");
  });

  it("normalizes xai to an OpenAI-compatible provider with the verified default baseURL", () => {
    expect(
      createModelProvider({
        kind: "xai",
        model: "grok-4",
      }).config,
    ).toMatchObject({
      kind: "xai",
      model: "grok-4",
      baseURL: "https://api.x.ai/v1",
      compatibilityMode: "compatible",
    });
  });

  it("normalizes response-native aliases to native compatibility mode", () => {
    expect(
      createModelProvider({
        kind: "aihubmix-responses",
        model: "gpt-4.1",
      }).config,
    ).toMatchObject({
      kind: "aihubmix-responses",
      model: "gpt-4.1",
      baseURL: "https://aihubmix.com/v1",
      compatibilityMode: "native",
    });
  });

  it("requires baseURL for customProvider", () => {
    expect(() =>
      createModelProvider({
        kind: "customProvider",
        model: "router-model",
      }),
    ).toThrow("requires an explicit baseURL");
  });

  it("adds a native OpenAI prompt cache key when stable prefix cache control is available", () => {
    expect(
      resolveProviderOptions(
        {
          kind: "openai",
          model: "gpt-5",
        },
        {
          cacheControl: {
            stablePrefixText: "shared system prefix",
          },
        },
      ),
    ).toMatchObject({
      openai: {
        promptCacheKey: expect.stringMatching(/^best-agent:openai:gpt-5:native:default:/),
      },
    });
  });

  it("keeps the OpenAI prompt cache key stable for the same provider fingerprint and stable prefix", () => {
    const first = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };
    const second = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };

    expect(first.openai.promptCacheKey).toBe(second.openai.promptCacheKey);
  });

  it("invalidates the OpenAI prompt cache key when the stable prefix changes", () => {
    const first = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "project memory A",
        },
      },
    ) as { openai: { promptCacheKey: string } };
    const second = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "project memory B",
        },
      },
    ) as { openai: { promptCacheKey: string } };

    expect(first.openai.promptCacheKey).not.toBe(second.openai.promptCacheKey);
  });

  it("invalidates the OpenAI prompt cache key when the model changes", () => {
    const first = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };
    const second = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5-mini",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };

    expect(first.openai.promptCacheKey).not.toBe(second.openai.promptCacheKey);
  });

  it("invalidates the OpenAI prompt cache key when the base URL changes", () => {
    const first = resolveProviderOptions(
      {
        baseURL: "https://api.openai.com/v1",
        compatibilityMode: "native",
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };
    const second = resolveProviderOptions(
      {
        baseURL: "https://api.openai.com/custom",
        compatibilityMode: "native",
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };

    expect(first.openai.promptCacheKey).not.toBe(second.openai.promptCacheKey);
  });

  it("invalidates the OpenAI prompt cache key when the provider kind changes", () => {
    const native = resolveProviderOptions(
      {
        kind: "openai",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };
    const responses = resolveProviderOptions(
      {
        kind: "openai-responses",
        model: "gpt-5",
      },
      {
        cacheControl: {
          stablePrefixText: "shared system prefix",
        },
      },
    ) as { openai: { promptCacheKey: string } };

    expect(native.openai.promptCacheKey).not.toBe(responses.openai.promptCacheKey);
  });

  it("does not send OpenAI prompt cache options to compatible gateways", () => {
    expect(
      resolveProviderOptions(
        {
          baseURL: "https://gateway.example/v1",
          compatibilityMode: "compatible",
          kind: "openai",
          model: "gpt-5",
        },
        {
          cacheControl: {
            stablePrefixText: "shared system prefix",
          },
        },
      ),
    ).toBeUndefined();
  });

  it("keeps Anthropic cache control support intact", () => {
    expect(
      resolveProviderOptions(
        {
          kind: "anthropic",
          model: "claude-sonnet",
        },
        {
          cacheControl: {
            stablePrefixText: "shared system prefix",
          },
        },
      ),
    ).toMatchObject({
      anthropic: {
        cacheControl: {
          type: "ephemeral",
        },
      },
    });
  });

  it("adds OpenAI-native reasoning controls for transcript-bearing requests", () => {
    expect(
      resolveProviderOptions(
        {
          compatibilityMode: "native",
          kind: "openai",
          model: "gpt-5.4",
          reasoningEffort: "high",
        },
        {
          includeReasoningSummary: true,
        },
      ),
    ).toMatchObject({
      openai: {
        reasoningEffort: "high",
        reasoningSummary: "auto",
      },
    });
  });

  it("maps native OpenAI Responses system prompts to top-level instructions", () => {
    expect(
      resolveProviderOptions(
        {
          compatibilityMode: "native",
          kind: "openai",
          model: "gpt-5.4",
        },
        {
          systemPrompt: "Follow the workspace policy.",
        },
      ),
    ).toEqual({
      openai: {
        instructions: "Follow the workspace policy.",
        systemMessageMode: "remove",
      },
    });
  });

  it("does not map system prompts to instructions for compatible OpenAI gateways", () => {
    expect(
      resolveProviderOptions(
        {
          baseURL: "https://gateway.example/v1",
          compatibilityMode: "compatible",
          kind: "openai",
          model: "gpt-5.4",
        },
        {
          systemPrompt: "Follow the workspace policy.",
        },
      ),
    ).toBeUndefined();
  });

  it("does not inject parallel tool call controls into native OpenAI provider options", () => {
    expect(
      resolveProviderOptions(
        {
          compatibilityMode: "native",
          kind: "openai",
          model: "gpt-5.4",
        },
        {},
      ),
    ).toBeUndefined();
  });

  it("keeps reasoning effort without forcing reasoning summaries for non-transcript requests", () => {
    expect(
      resolveProviderOptions(
        {
          compatibilityMode: "native",
          kind: "openai",
          model: "gpt-5.4",
          reasoningEffort: "medium",
        },
        {},
      ),
    ).toEqual({
      openai: {
        reasoningEffort: "medium",
      },
    });
  });

  it("defaults transcript-bearing OpenAI-native reasoning requests to medium effort", () => {
    expect(
      resolveProviderOptions(
        {
          compatibilityMode: "native",
          kind: "openai",
          model: "gpt-5.4",
        },
        {
          includeReasoningSummary: true,
        },
      ),
    ).toEqual({
      openai: {
        reasoningEffort: "medium",
        reasoningSummary: "auto",
      },
    });
  });

  it("reports native prompt-cache eligibility for supported providers", () => {
    expect(
      resolveProviderPromptCacheDiagnostics(
        {
          kind: "openai",
          model: "gpt-5",
        },
        {
          stablePrefixText: "shared system prefix",
        },
      ),
    ).toEqual({
      eligible: true,
      providerHintApplied: true,
    });

    expect(
      resolveProviderPromptCacheDiagnostics(
        {
          kind: "anthropic",
          model: "claude-sonnet",
        },
        {
          stablePrefixText: "shared system prefix",
        },
      ),
    ).toEqual({
      eligible: true,
      providerHintApplied: true,
    });
  });

  it("marks compatible gateways as ineligible for native prompt-cache hints", () => {
    expect(
      resolveProviderPromptCacheDiagnostics(
        {
          baseURL: "https://gateway.example/v1",
          compatibilityMode: "compatible",
          kind: "openai",
          model: "gpt-5",
        },
        {
          stablePrefixText: "shared system prefix",
        },
      ),
    ).toEqual({
      eligible: false,
      providerHintApplied: false,
    });
  });

  it("uses an explicit provider timeout when configured", () => {
    expect(
      resolveProviderTimeoutMs({
        kind: "openai",
        model: "gpt-5",
        timeoutMs: 120_000,
      }),
    ).toBe(120_000);
  });

  it("falls back to the default provider timeout when config is absent or invalid", () => {
    expect(
      resolveProviderTimeoutMs({
        kind: "openai",
        model: "gpt-5",
      }),
    ).toBe(300_000);
    expect(
      resolveProviderTimeoutMs({
        kind: "openai",
        model: "gpt-5",
        timeoutMs: 0,
      }),
    ).toBe(300_000);
  });

  it("marks native OpenAI-compatible providers as background-capable", () => {
    expect(
      getProviderCapabilities({
        kind: "openai",
        model: "gpt-5",
      }),
    ).toMatchObject({
      supportsBackgroundJobs: true,
      supportsNativeWebSearch: true,
      supportsResumableStream: true,
      supportsWebhookCompletion: true,
    });
  });

  it("marks compatible gateways as foreground-only even under OpenAI-compatible families", () => {
    expect(
      getProviderCapabilities({
        baseURL: "https://gateway.example/v1",
        compatibilityMode: "compatible",
        kind: "openai",
        model: "gpt-5",
      }),
    ).toMatchObject({
      supportsBackgroundJobs: false,
      supportsNativeWebSearch: false,
      supportsPauseResume: false,
      supportsResumableStream: false,
      supportsWebhookCompletion: false,
    });
  });

  it("marks anthropic providers as pause-resume capable", () => {
    const provider = createModelProvider({
      kind: "anthropic",
      model: "claude-sonnet",
    });
    expect(provider.capabilities).toMatchObject({
      supportsBackgroundJobs: false,
      supportsNativeWebSearch: false,
      supportsPauseResume: true,
      supportsResumableStream: false,
      supportsWebhookCompletion: false,
    });
  });
});
