import { ArgumentsHost, HttpStatus } from '@nestjs/common';
import { ApiCode } from '@pac/types';
import { AllExceptionsFilter } from '../src/common/filters/all-exceptions.filter';

/**
 * 请求体超限回执契约(2026-07 FRIDAY 增量 push 线上问题)。
 *
 * body-parser 的 PayloadTooLargeError 是**裸 Error**,不拦就掉进 500/90000 分支 ——
 * 对面按接入文档 §9.1 把 9xxxx 当"PAC 内部错误 → 退避重试",于是整点重试永远重试不好。
 * 本文件锁住:超限必须回 10002 + HTTP 200,且带可执行的提示。
 */
function makeHost() {
  const json = jest.fn();
  const status = jest.fn().mockReturnValue({ json });
  const host = {
    switchToHttp: () => ({
      getResponse: () => ({ status }),
      getRequest: () => ({ method: 'POST', url: '/pac/v1/push/rows' }),
    }),
  } as unknown as ArgumentsHost;
  return { host, status, json };
}

/** 复刻 raw-body 抛的形状(它不导出类型,只能按特征字段构造) */
function payloadTooLarge(shape: 'type' | 'status' | 'statusCode') {
  const e = new Error('request entity too large') as Error & Record<string, unknown>;
  if (shape === 'type') e.type = 'entity.too.large';
  if (shape === 'status') e.status = 413;
  if (shape === 'statusCode') e.statusCode = 413;
  return e;
}

describe('AllExceptionsFilter — 请求体超限', () => {
  const filter = new AllExceptionsFilter();

  it.each(['type', 'status', 'statusCode'] as const)(
    '⭐ 按 %s 特征识别 → 10002 + HTTP 200(不是 90000/500)',
    (shape) => {
      const { host, status, json } = makeHost();
      filter.catch(payloadTooLarge(shape), host);

      expect(status).toHaveBeenCalledWith(HttpStatus.OK); // 文档:HTTP 恒为 200
      const body = json.mock.calls[0][0];
      expect(body.code).toBe(ApiCode.CLIENT_VALIDATION_FAILED); // 10002 = 批量超限,修正后重发
      expect(body.code).not.toBe(ApiCode.INTERNAL_ERROR); // 不能再是 90000
      expect(body.data).toBeNull();
    },
  );

  it('回执要能指导对面动作(带上限 + 建议批量)', () => {
    const { host, json } = makeHost();
    filter.catch(payloadTooLarge('type'), host);
    const body = json.mock.calls[0][0];
    expect(body.msg).toContain('10MB');
    expect(body.msg).toContain('减小单批行数');
    expect(body.details).toMatchObject({ limit: '10MB' });
  });

  it('普通 Error 不受影响 —— 仍是 90000 + HTTP 500(基础设施要能看见真故障)', () => {
    const { host, status, json } = makeHost();
    filter.catch(new Error('boom'), host);
    expect(status).toHaveBeenCalledWith(HttpStatus.INTERNAL_SERVER_ERROR);
    expect(json.mock.calls[0][0].code).toBe(ApiCode.INTERNAL_ERROR);
  });

  it('不能误伤:仅消息里含 "too large" 但无特征字段的普通 Error 仍走 500', () => {
    const { host, status } = makeHost();
    filter.catch(new Error('some payload is too large for the queue'), host);
    expect(status).toHaveBeenCalledWith(HttpStatus.INTERNAL_SERVER_ERROR);
  });
});
