import { ConfigService } from '@nestjs/config';
import { ThirdPartyClientService } from './third-party-client.service';

describe('ThirdPartyClientService', () => {
  const save = jest.fn();
  const create = jest.fn((input) => input);
  const repository = { create, save };
  const config = {
    getOrThrow: jest.fn((key: string) => {
      if (key === 'WK_BASE_URL') {
        return 'https://biedawo.org/api.php';
      }
      throw new Error(`Missing config ${key}`);
    }),
    get: jest.fn((key: string) => {
      const values: Record<string, string> = {
        WK_BASE_URL: 'https://biedawo.org/api.php',
        WK_APP_UID: '10001',
        WK_APP_KEY: 'plain-key',
      };
      return values[key];
    }),
  };
  const debugConfig = {
    ...config,
    get: jest.fn((key: string) => {
      const values: Record<string, string> = {
        WK_BASE_URL: 'https://biedawo.org/api.php',
        WK_APP_UID: '10001',
        WK_APP_KEY: 'plain-key',
        WK_DEBUG_LOG: 'true',
      };
      return values[key];
    }),
  };

  beforeEach(() => {
    jest.clearAllMocks();
  });

  afterEach(() => {
    jest.restoreAllMocks();
  });

  it('writes a masked failure log when third-party business response fails', async () => {
    const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: true,
      status: 200,
      text: async () => JSON.stringify({ code: 0, msg: '无效账号' }),
    } as Response);
    const service = new ThirdPartyClientService(
      config as unknown as ConfigService,
      repository as never,
    );

    await expect(
      service.call('get', {
        platform: 100,
        user: 'student001',
        pass: 'student-password',
      }),
    ).rejects.toThrow('第三方接口返回失败：无效账号');

    expect(fetchMock).toHaveBeenCalledWith(
      expect.objectContaining({
        href: 'https://biedawo.org/api.php?act=get',
      }),
      expect.objectContaining({
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: expect.any(URLSearchParams),
      }),
    );
    const [, requestInit] = fetchMock.mock.calls[0];
    expect((requestInit?.body as URLSearchParams).get('uid')).toBe('10001');
    expect((requestInit?.body as URLSearchParams).get('key')).toBe('plain-key');
    expect((requestInit?.body as URLSearchParams).get('act')).toBeNull();
    expect(save).toHaveBeenCalledWith(
      expect.objectContaining({
        act: 'get',
        status: 'failure',
        httpStatus: 200,
        requestPayload: expect.objectContaining({
          key: 'pl****ey',
          pass: 'st****rd',
        }),
        responsePayload: { code: 0, msg: '无效账号' },
        errorMessage: '无效账号',
      }),
    );
  });

  it('exposes safe third-party config status', () => {
    const service = new ThirdPartyClientService(
      config as unknown as ConfigService,
      repository as never,
    );

    expect(service.getConfigStatus()).toEqual({
      provider: 'biedawo',
      baseUrl: 'https://biedawo.org/api.php',
      uidConfigured: true,
      keyConfigured: true,
      debugLogEnabled: false,
    });
  });

  it('writes a failure log and returns a client error when network request fails', async () => {
    jest
      .spyOn(global, 'fetch')
      .mockRejectedValue(new TypeError('fetch failed'));
    const service = new ThirdPartyClientService(
      config as unknown as ConfigService,
      repository as never,
    );

    await expect(service.call('getcate')).rejects.toThrow('fetch failed');

    expect(save).toHaveBeenCalledWith(
      expect.objectContaining({
        act: 'getcate',
        status: 'failure',
        httpStatus: null,
        responsePayload: null,
        errorMessage: 'fetch failed',
      }),
    );
  });

  it('prints masked request diagnostics when debug logging is enabled', async () => {
    jest.spyOn(global, 'fetch').mockResolvedValue({
      ok: true,
      status: 200,
      text: async () => JSON.stringify({ code: 1, data: [] }),
    } as Response);
    const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
    const service = new ThirdPartyClientService(
      debugConfig as unknown as ConfigService,
      repository as never,
    );

    await service.call('getcate');

    expect(consoleSpy).toHaveBeenCalledWith(
      '[third-party] request',
      expect.objectContaining({
        act: 'getcate',
        method: 'POST',
        url: 'https://biedawo.org/api.php?act=getcate',
        body: expect.objectContaining({
          uid: '10001',
          key: 'pl****ey',
        }),
        keyLength: 9,
        keyPreview: 'plai****-key',
        keyHasOuterWhitespace: false,
      }),
    );
  });
});
