import { act, renderHook, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { ReactNode } from 'react';
import { AllProviders } from '../../setup/test-utils';
import { useCodex } from '@/hooks/use-codex';

function createJsonResponse(body: Record<string, unknown>, status = 200): Response {
  return new Response(JSON.stringify(body), {
    status,
    headers: { 'Content-Type': 'application/json' },
  });
}

const diagnosticsResponse = {
  binary: {
    installed: true,
    path: '/tmp/codex',
    installDir: '/tmp',
    source: 'PATH',
    version: 'codex-cli 0.118.0-alpha.3',
    overridePath: null,
    supportsConfigOverrides: true,
  },
  file: {
    label: 'Codex user config',
    path: '$CODEX_HOME/config.toml',
    resolvedPath: '/tmp/.codex/config.toml',
    exists: true,
    isSymlink: false,
    isRegularFile: true,
    sizeBytes: 64,
    mtimeMs: 100,
    parseError: null,
    readError: null,
  },
  workspacePath: '/tmp/workspace',
  config: {
    model: 'gpt-5.3-codex',
    modelReasoningEffort: null,
    modelContextWindow: null,
    modelAutoCompactTokenLimit: null,
    modelProvider: null,
    activeProfile: null,
    approvalPolicy: null,
    sandboxMode: null,
    webSearch: null,
    toolOutputTokenLimit: null,
    personality: null,
    topLevelKeys: ['model'],
    profileCount: 0,
    profileNames: [],
    modelProviderCount: 0,
    modelProviders: [],
    featureCount: 0,
    enabledFeatures: [],
    disabledFeatures: [],
    trustedProjectCount: 0,
    untrustedProjectCount: 0,
    projectTrust: [],
    mcpServerCount: 0,
    mcpServers: [],
  },
  supportMatrix: [],
  warnings: [],
  docsReference: {
    providerValues: [],
    settingsHierarchy: [],
    notes: [],
    links: [],
    providerDocs: [],
  },
};

const initialRawConfigResponse = {
  path: '$CODEX_HOME/config.toml',
  resolvedPath: '/tmp/.codex/config.toml',
  exists: true,
  mtime: 100,
  rawText: 'model = "gpt-5.3-codex"\n',
  config: { model: 'gpt-5.3-codex' },
  parseError: null,
  readError: null,
};

const patchedRawConfigResponse = {
  success: true,
  path: '$CODEX_HOME/config.toml',
  resolvedPath: '/tmp/.codex/config.toml',
  exists: true,
  mtime: 200,
  rawText: 'model = "gpt-5.4"\n',
  config: { model: 'gpt-5.4' },
  parseError: null,
  readError: null,
};

const wrapper = ({ children }: { children: ReactNode }) => <AllProviders>{children}</AllProviders>;

describe('useCodex', () => {
  afterEach(() => {
    vi.unstubAllGlobals();
    vi.restoreAllMocks();
  });

  it('updates cached raw config immediately after a structured patch save', async () => {
    const fetchMock = vi.fn((input: RequestInfo | URL, init?: RequestInit) => {
      const url = String(input);

      if (url.endsWith('/api/codex/diagnostics')) {
        return Promise.resolve(createJsonResponse(diagnosticsResponse));
      }

      if (url.endsWith('/api/codex/config/raw') && !init?.method) {
        return Promise.resolve(createJsonResponse(initialRawConfigResponse));
      }

      if (url.endsWith('/api/codex/config/patch')) {
        return Promise.resolve(createJsonResponse(patchedRawConfigResponse));
      }

      return Promise.reject(new Error(`Unexpected fetch: ${url}`));
    });

    vi.stubGlobal('fetch', fetchMock);

    const { result } = renderHook(() => useCodex(), { wrapper });

    await waitFor(() => expect(result.current.rawConfig?.mtime).toBe(100));

    await act(async () => {
      await result.current.patchConfigAsync({
        kind: 'top-level',
        values: { model: 'gpt-5.4' },
        expectedMtime: 100,
      });
    });

    await waitFor(() => expect(result.current.rawConfig?.mtime).toBe(200));

    expect(result.current.rawConfig?.rawText).toBe('model = "gpt-5.4"\n');
    expect(result.current.rawConfig?.config?.model).toBe('gpt-5.4');
    expect(
      fetchMock.mock.calls.filter(([input]) => String(input).endsWith('/api/codex/config/raw'))
    ).toHaveLength(1);
  });
});
