/**
 * CircuitBreaker 熔断器单元测试
 * 测试状态转换: CLOSED → OPEN → HALF_OPEN → CLOSED
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { CircuitBreaker, CircuitBreakerOpenError, CircuitState } from '@/common/circuit-breaker';

describe('CircuitBreaker', () => {
  let breaker: CircuitBreaker;

  beforeEach(() => {
    breaker = new CircuitBreaker({
      name: 'test-provider',
      failureThreshold: 3,
      cooldownMs: 1000, // 1秒冷却，方便测试
      successThreshold: 2,
    });
  });

  describe('初始状态', () => {
    it('初始状态为 CLOSED', () => {
      expect(breaker.getState()).toBe('CLOSED');
      expect(breaker.isOpen()).toBe(false);
    });
  });

  describe('CLOSED → OPEN 状态转换', () => {
    it('连续失败 3 次后进入 OPEN 状态', async () => {
      for (let i = 0; i < 3; i++) {
        await expect(breaker.call(() => Promise.reject(new Error('fail')))).rejects.toThrow('fail');
      }
      expect(breaker.getState()).toBe('OPEN');
      expect(breaker.isOpen()).toBe(true);
    });

    it('OPEN 状态拒绝所有请求', async () => {
      // 先触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(breaker.getState()).toBe('OPEN');

      // 再次调用应抛出 CircuitBreakerOpenError
      await expect(breaker.call(() => Promise.resolve('success'))).rejects.toThrow(CircuitBreakerOpenError);
    });

    it('OPEN 状态下调用抛出包含剩余冷却时间', async () => {
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }

      try {
        await breaker.call(() => Promise.resolve('success'));
      } catch (e) {
        expect(e).toBeInstanceOf(CircuitBreakerOpenError);
        expect((e as CircuitBreakerOpenError).remainingMs).toBeGreaterThan(0);
        expect((e as CircuitBreakerOpenError).breakerName).toBe('test-provider');
      }
    });

    it('失败计数在成功后重置', async () => {
      // 失败 2 次（未达到阈值）
      await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      expect(breaker.getState()).toBe('CLOSED');

      // 成功后，失败计数应重置
      await breaker.call(() => Promise.resolve('success'));
      expect(breaker.getState()).toBe('CLOSED');

      // 再失败 3 次才会熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(breaker.getState()).toBe('OPEN');
    });
  });

  describe('OPEN → HALF_OPEN 状态转换', () => {
    it('冷却期过后自动进入 HALF_OPEN', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(breaker.getState()).toBe('OPEN');

      // 等待冷却期
      await new Promise((resolve) => setTimeout(resolve, 1100));

      // 冷却期过后应自动进入 HALF_OPEN
      expect(breaker.getState()).toBe('HALF_OPEN');
    });

    it('HALF_OPEN 状态下可以接受请求', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      await new Promise((resolve) => setTimeout(resolve, 1100));
      expect(breaker.getState()).toBe('HALF_OPEN');

      // HALF_OPEN 状态下调用应成功
      const result = await breaker.call(() => Promise.resolve('success'));
      expect(result).toBe('success');
    });
  });

  describe('HALF_OPEN → CLOSED 状态转换', () => {
    it('HALF_OPEN 连续成功 2 次后恢复 CLOSED', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      await new Promise((resolve) => setTimeout(resolve, 1100));
      expect(breaker.getState()).toBe('HALF_OPEN');

      // 第一次成功
      await breaker.call(() => Promise.resolve('success1'));
      expect(breaker.getState()).toBe('HALF_OPEN');

      // 第二次成功
      await breaker.call(() => Promise.resolve('success2'));
      expect(breaker.getState()).toBe('CLOSED');
    });

    it('HALF_OPEN 失败 1 次立即回到 OPEN', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      await new Promise((resolve) => setTimeout(resolve, 1100));
      expect(breaker.getState()).toBe('HALF_OPEN');

      // 失败
      await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      expect(breaker.getState()).toBe('OPEN');
    });

    it('HALF_OPEN 失败后重新开始冷却计时', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      await new Promise((resolve) => setTimeout(resolve, 1100));

      // 进入 HALF_OPEN 后失败
      await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      expect(breaker.getState()).toBe('OPEN');

      // 此时 getState() 不应立即进入 HALF_OPEN（需要等待新的冷却期）
      expect(breaker.getState()).toBe('OPEN');
    });
  });

  describe('reset() 方法', () => {
    it('reset() 恢复到 CLOSED 状态', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(breaker.getState()).toBe('OPEN');

      // 重置
      breaker.reset();
      expect(breaker.getState()).toBe('CLOSED');
      expect(breaker.isOpen()).toBe(false);
    });

    it('reset() 清除所有计数', async () => {
      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      breaker.reset();

      // 重置后应该可以正常调用
      const result = await breaker.call(() => Promise.resolve('success'));
      expect(result).toBe('success');
      expect(breaker.getState()).toBe('CLOSED');
    });
  });

  describe('isOpen() 方法', () => {
    it('CLOSED 状态返回 false', () => {
      expect(breaker.isOpen()).toBe(false);
    });

    it('OPEN 状态返回 true', async () => {
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(breaker.isOpen()).toBe(true);
    });

    it('HALF_OPEN 状态返回 false', async () => {
      for (let i = 0; i < 3; i++) {
        await breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      await new Promise((resolve) => setTimeout(resolve, 1100));
      expect(breaker.getState()).toBe('HALF_OPEN');
      expect(breaker.isOpen()).toBe(false);
    });
  });

  describe('自定义配置', () => {
    it('使用自定义 failureThreshold', async () => {
      const customBreaker = new CircuitBreaker({
        name: 'custom',
        failureThreshold: 5,
        cooldownMs: 1000,
      });

      for (let i = 0; i < 5; i++) {
        await customBreaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(customBreaker.getState()).toBe('OPEN');
    });

    it('使用自定义 cooldownMs', async () => {
      const customBreaker = new CircuitBreaker({
        name: 'custom-cooldown',
        failureThreshold: 3,
        cooldownMs: 500, // 500ms
      });

      for (let i = 0; i < 3; i++) {
        await customBreaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }
      expect(customBreaker.getState()).toBe('OPEN');

      // 等待 500ms
      await new Promise((resolve) => setTimeout(resolve, 600));
      expect(customBreaker.getState()).toBe('HALF_OPEN');
    });
  });

  describe('错误传播', () => {
    it('原始错误被正确传播', async () => {
      const customError = new Error('Custom error message');
      await expect(breaker.call(() => Promise.reject(customError))).rejects.toThrow('Custom error message');
    });

    it('同步错误也被正确处理', async () => {
      await expect(breaker.call(() => { throw new Error('sync error'); })).rejects.toThrow('sync error');
    });
  });
});

describe('CircuitBreakerOpenError', () => {
  it('包含正确的错误信息', () => {
    const error = new CircuitBreakerOpenError('test-provider', 5000);
    expect(error.message).toContain('test-provider');
    expect(error.message).toContain('5s');
    expect(error.breakerName).toBe('test-provider');
    expect(error.remainingMs).toBe(5000);
  });

  it('继承自 Error', () => {
    const error = new CircuitBreakerOpenError('test', 1000);
    expect(error).toBeInstanceOf(Error);
    expect(error.name).toBe('CircuitBreakerOpenError');
  });
});