import { DEFAULT_CLAUDE_BACKEND, DEFAULT_CLAUDE_MODEL } from "./alternativeBackendCleanup";

export const LEGACY_CODEX_BACKEND = "codex-subscription";
export const LEGACY_NATIVE_CODEX_BACKEND = "experimental-codex";
export const NATIVE_CODEX_BACKEND = "codex";
export const FIRST_CLASS_BACKENDS_PROMOTION_KEY = "agent_backends_first_class_promoted";

export type SettingEntry = readonly [string, string];

export interface CodexBackendGateMigrationInput {
  enableNative: boolean;
  defaultBackend: string | null;
  sessionProviders: readonly SettingEntry[];
  selectedProviders: Readonly<Record<string, string>>;
}

export interface CodexBackendGateMigrationPlan {
  fromBackend: string;
  toBackend: string;
  toModel: string | null;
  defaultBackend: string | null;
  resetDefault: boolean;
  sessionIds: string[];
}

export interface CodexBackendMigrationModel {
  id: string;
}

export interface CodexBackendMigrationBackend {
  id: string;
  default_model: string | null;
  manual_models: readonly CodexBackendMigrationModel[];
  discovered_models: readonly CodexBackendMigrationModel[];
}

export interface CodexBackendMigrationModelInput {
  plan: Pick<CodexBackendGateMigrationPlan, "toBackend" | "toModel">;
  sessionId: string;
  persistedModels: ReadonlyMap<string, string>;
  selectedModels: Readonly<Record<string, string>>;
  backends: readonly CodexBackendMigrationBackend[];
}

export interface BackendGateLoadInput {
  alternativeBackendsCompiled: boolean;
  alternativeBackendsSetting: string | null;
  codexSetting: string | null;
  promotionSetting: string | null;
}

export interface BackendGateLoadPlan {
  alternativeBackendsEnabled: boolean;
  codexEnabled: boolean;
  shouldPersistPromotion: boolean;
}

type GateSettingLoad =
  | { status: "fulfilled"; value: string | null }
  | { status: "rejected"; reason: unknown };

function settingSessionId(key: string, prefix: string): string | null {
  return key.startsWith(prefix) ? key.slice(prefix.length) : null;
}

function modelsForBackend(
  backend: CodexBackendMigrationBackend | undefined,
): readonly CodexBackendMigrationModel[] {
  if (!backend) return [];
  return backend.discovered_models.length > 0
    ? backend.discovered_models
    : backend.manual_models;
}

function fallbackModelForBackend(
  backend: CodexBackendMigrationBackend | undefined,
): string | null {
  const models = modelsForBackend(backend);
  if (!backend) return null;
  if (backend.default_model && models.some((model) => model.id === backend.default_model)) {
    return backend.default_model;
  }
  return models[0]?.id ?? null;
}

function shouldMigrateCodexBackend(provider: string | null, enableNative: boolean): boolean {
  if (!provider) return false;
  if (enableNative) {
    return provider === LEGACY_CODEX_BACKEND || provider === LEGACY_NATIVE_CODEX_BACKEND;
  }
  return (
    provider === NATIVE_CODEX_BACKEND ||
    provider === LEGACY_NATIVE_CODEX_BACKEND ||
    provider === LEGACY_CODEX_BACKEND
  );
}

export function planBackendGateLoad({
  alternativeBackendsCompiled,
  alternativeBackendsSetting,
  codexSetting,
  promotionSetting,
}: BackendGateLoadInput): BackendGateLoadPlan {
  if (!alternativeBackendsCompiled) {
    return {
      alternativeBackendsEnabled: false,
      codexEnabled: false,
      shouldPersistPromotion: false,
    };
  }

  if (promotionSetting !== "true") {
    return {
      alternativeBackendsEnabled: true,
      codexEnabled: true,
      shouldPersistPromotion: true,
    };
  }

  const codexEnabled =
    codexSetting !== "false";
  const alternativeBackendsEnabled =
    alternativeBackendsSetting !== "false";

  return {
    alternativeBackendsEnabled,
    codexEnabled,
    shouldPersistPromotion: false,
  };
}

export function planBackendGateLoadFromResults({
  alternativeBackendsCompiled,
  alternativeBackendsSetting,
  codexSetting,
  promotionSetting,
}: {
  alternativeBackendsCompiled: boolean;
  alternativeBackendsSetting: GateSettingLoad;
  codexSetting: GateSettingLoad;
  promotionSetting: GateSettingLoad;
}): BackendGateLoadPlan | null {
  if (
    alternativeBackendsSetting.status === "rejected" ||
    codexSetting.status === "rejected" ||
    promotionSetting.status === "rejected"
  ) {
    return null;
  }

  return planBackendGateLoad({
    alternativeBackendsCompiled,
    alternativeBackendsSetting: alternativeBackendsSetting.value,
    codexSetting: codexSetting.value,
    promotionSetting: promotionSetting.value,
  });
}

export function planCodexBackendGateMigration({
  enableNative,
  defaultBackend,
  sessionProviders,
  selectedProviders,
}: CodexBackendGateMigrationInput): CodexBackendGateMigrationPlan {
  const fromBackend = enableNative ? LEGACY_CODEX_BACKEND : NATIVE_CODEX_BACKEND;
  const toBackend = enableNative ? NATIVE_CODEX_BACKEND : DEFAULT_CLAUDE_BACKEND;
  const toModel = enableNative ? null : DEFAULT_CLAUDE_MODEL;
  const sessionIds = new Set<string>();

  for (const [key, value] of sessionProviders) {
    const sessionId = settingSessionId(key, "model_provider:");
    if (sessionId && shouldMigrateCodexBackend(value, enableNative)) sessionIds.add(sessionId);
  }
  for (const [sessionId, provider] of Object.entries(selectedProviders)) {
    if (shouldMigrateCodexBackend(provider, enableNative)) sessionIds.add(sessionId);
  }
  const resetDefault = shouldMigrateCodexBackend(defaultBackend, enableNative);

  return {
    fromBackend,
    toBackend,
    toModel,
    defaultBackend: resetDefault ? toBackend : defaultBackend,
    resetDefault,
    sessionIds: [...sessionIds].sort(),
  };
}

export function resolveCodexBackendMigrationModel({
  plan,
  sessionId,
  persistedModels,
  selectedModels,
  backends,
}: CodexBackendMigrationModelInput): string | null {
  if (plan.toModel) return plan.toModel;

  const backend = backends.find((candidate) => candidate.id === plan.toBackend);
  const models = modelsForBackend(backend);
  const candidate = persistedModels.get(sessionId) ?? selectedModels[sessionId] ?? null;
  if (candidate && models.some((model) => model.id === candidate)) {
    return candidate;
  }

  return fallbackModelForBackend(backend);
}
