/*
 * Copyright 2025 coze-dev Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { type AxiosError, AxiosHeaders } from 'axios';
import { logger } from '@coze-arch/logger';

import {
  reportHttpError,
  ReportEventNames,
  ApiError,
  isApiError,
} from '../src/api-error';

vi.mock('@coze-arch/logger', () => ({
  logger: {
    info: vi.fn(),
    persist: {
      error: vi.fn(),
    },
  },
}));
describe('reportHttpError', () => {
  const error: AxiosError = {
    response: {
      data: {
        code: '500',
        msg: 'Internal Server Error',
      },
      status: 500,
      statusText: '',
      config: {
        headers: new AxiosHeaders(),
      },
      headers: {
        'x-tt-logid': '1234567890',
      },
    },
    config: {
      method: 'GET',
      headers: new AxiosHeaders(),
      url: '/users',
    },
    message: 'Request failed with status code 500',
    name: 'AxiosError',
    isAxiosError: true,
    toJSON: () => ({}),
  };
  it('if no response data, should report http error', () => {
    const eventName = ReportEventNames.ApiError;
    const noResponseError: AxiosError = {
      response: {
        data: {
          code: '',
        },
        status: 500,
        statusText: '',
        config: {
          headers: new AxiosHeaders(),
        },
        headers: {
          'x-tt-logid': '1234567890',
        },
      },
      config: {
        method: 'GET',
        headers: new AxiosHeaders(),
        url: '/users',
      },
      message: 'Request failed with status code 500',
      name: 'AxiosError',
      isAxiosError: true,
      toJSON: () => ({}),
    };

    reportHttpError(eventName, noResponseError);

    expect(logger.persist.error).toBeCalledWith({
      eventName,
      error: noResponseError,
      meta: {
        message: error.message,
        name: error.name,
        httpStatusCode: '500',
        httpMethod: 'GET',
        urlPath: '/users',
        logId: '1234567890',
        customErrorCode: '',
        customErrorMsg: '',
      },
    });
  });

  it('should report http error', () => {
    const eventName = ReportEventNames.ApiError;

    reportHttpError(eventName, error);

    expect(logger.persist.error).toHaveBeenCalledWith({
      eventName,
      error,
      meta: {
        message: error.message,
        name: error.name,
        httpStatusCode: '500',
        httpMethod: 'GET',
        urlPath: '/users',
        logId: '1234567890',
        customErrorCode: '500',
        customErrorMsg: 'Internal Server Error',
      },
    });
  });

  it('should handle error when reporting http catch', () => {
    const eventName = ReportEventNames.ApiError;

    (logger.persist.error as any).mockImplementation(() => {
      throw new Error('Failed to persist error');
    });

    expect(() => {
      reportHttpError(eventName, error);
    }).toThrowError('Failed to persist error');
  });
});

describe('isApiError', () => {
  it('should return true if error is an instance of ApiError', () => {
    const error = new ApiError('500', 'Internal Server Error', {
      data: {},
      status: 500,
      statusText: 'Internal Server Error',
      headers: new AxiosHeaders(),
      config: {
        headers: new AxiosHeaders(),
      },
    });
    const result = isApiError(error);
    expect(result).toBe(true);
  });

  it('should return false if error is not an instance of ApiError', () => {
    const error = new Error('OtherError');
    const result = isApiError(error);
    expect(result).toBe(false);
  });
});
