/**
 * ProviderRegistry 单元测试
 * 测试供应商注册、查找、轮转、熔断集成
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ProviderRegistry, ProviderNode } from '@/common/provider-registry';
import { CircuitBreaker } from '@/common/circuit-breaker';

interface MockProvider {
  readonly name: string;
  readonly vendor: string;
}

const createMockProvider = (name: string, vendor: string = 'mock'): MockProvider => ({
  name,
  vendor,
});

describe('ProviderRegistry', () => {
  let registry: ProviderRegistry<MockProvider>;

  beforeEach(() => {
    registry = new ProviderRegistry<MockProvider>();
  });

  describe('注册与注销', () => {
    it('注册一个 provider', () => {
      const provider = createMockProvider('provider-a');
      registry.register(provider);

      expect(registry.get('provider-a')).toBeDefined();
      expect(registry.get('provider-a')?.provider.name).toBe('provider-a');
    });

    it('注册多个 provider', () => {
      registry.register(createMockProvider('a'));
      registry.register(createMockProvider('b'));
      registry.register(createMockProvider('c'));

      expect(registry.size).toBe(3);
    });

    it('同名 provider 覆盖', () => {
      registry.register(createMockProvider('same'));
      registry.register(createMockProvider('same'));

      expect(registry.size).toBe(1);
    });

    it('注销已存在的 provider', () => {
      registry.register(createMockProvider('to-remove'));
      expect(registry.get('to-remove')).toBeDefined();

      const result = registry.unregister('to-remove');
      expect(result).toBe(true);
      expect(registry.get('to-remove')).toBeUndefined();
    });

    it('注销不存在的 provider 返回 false', () => {
      const result = registry.unregister('nonexistent');
      expect(result).toBe(false);
    });
  });

  describe('查找', () => {
    beforeEach(() => {
      registry.register(createMockProvider('provider-a', 'vendor-x'));
      registry.register(createMockProvider('provider-b', 'vendor-x'));
      registry.register(createMockProvider('provider-c', 'vendor-y'));
    });

    it('按名称查找', () => {
      const node = registry.get('provider-a');
      expect(node?.provider.name).toBe('provider-a');
    });

    it('按供应商查找', () => {
      const node = registry.getByVendor('vendor-x');
      expect(node?.provider.name).toBe('provider-a');
    });

    it('获取所有节点', () => {
      const all = registry.list();
      expect(all.length).toBe(3);
    });

    it('过滤器筛选', () => {
      const enabledOnly = registry.list(n => n.enabled);
      expect(enabledOnly.length).toBe(3);
    });
  });

  describe('状态查询', () => {
    beforeEach(() => {
      registry.register(createMockProvider('enabled-provider'));
      registry.register(createMockProvider('disabled-provider'), { enabled: false });
    });

    it('listEnabled 返回已启用节点', () => {
      const enabled = registry.listEnabled();
      expect(enabled.length).toBe(1);
      expect(enabled[0].provider.name).toBe('enabled-provider');
    });

    it('listAvailable 返回可用节点（启用+未熔断+未耗尽）', () => {
      const available = registry.listAvailable();
      expect(available.length).toBe(1);
      expect(available[0].provider.name).toBe('enabled-provider');
    });

    it('listBroken 返回已熔断节点', async () => {
      const broken = registry.listBroken();
      expect(broken.length).toBe(0);

      // 触发熔断
      const node = registry.get('enabled-provider');
      for (let i = 0; i < 3; i++) {
        await node?.breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }

      // 确保状态更新
      await new Promise(resolve => setTimeout(resolve, 50));

      const brokenAfter = registry.listBroken();
      expect(brokenAfter.length).toBe(1);
    });

    it('listExhausted 返回额度耗尽节点', () => {
      registry.markExhausted('enabled-provider', 'quota exceeded', 0);

      const exhausted = registry.listExhausted();
      expect(exhausted.length).toBe(1);
      expect(exhausted[0].provider.name).toBe('enabled-provider');
    });
  });

  describe('额度耗尽管理', () => {
    beforeEach(() => {
      registry.register(createMockProvider('provider'));
    });

    it('markExhausted 标记供应商额度耗尽', () => {
      registry.markExhausted('provider', 'Daily quota exceeded', 0);

      const node = registry.get('provider');
      expect(node?.exhausted).toBe(true);
      expect(node?.exhaustedReason).toBe('Daily quota exceeded');
    });

    it('markExhausted 带 TTL 自动恢复', async () => {
      registry.markExhausted('provider', 'temp', 100);

      const node = registry.get('provider');
      expect(node?.exhausted).toBe(true);

      await new Promise(resolve => setTimeout(resolve, 150));
      expect(node?.exhausted).toBe(false);
    });

    it('clearExhausted 清除耗尽标记', () => {
      registry.markExhausted('provider', 'quota', 0);
      expect(registry.get('provider')?.exhausted).toBe(true);

      const result = registry.clearExhausted('provider');
      expect(result).toBe(true);
      expect(registry.get('provider')?.exhausted).toBe(false);
    });

    it('clearAllExhausted 清除所有耗尽标记', () => {
      registry.register(createMockProvider('p1'));
      registry.register(createMockProvider('p2'));

      registry.markExhausted('p1', 'q1', 0);
      registry.markExhausted('p2', 'q2', 0);

      registry.clearAllExhausted();

      expect(registry.listExhausted().length).toBe(0);
    });
  });

  describe('轮转调度', () => {
    beforeEach(() => {
      registry.register(createMockProvider('p1'));
      registry.register(createMockProvider('p2'));
      registry.register(createMockProvider('p3'));
    });

    it('getNextAvailable 返回下一个可用 provider', () => {
      const next = registry.getNextAvailable('p1');
      expect(next?.provider.name).toBe('p2');
    });

    it('轮转到末尾后从头开始', () => {
      const next = registry.getNextAvailable('p3');
      expect(next?.provider.name).toBe('p1');
    });

    it('当前 provider 不可用时返回第一个可用', () => {
      // 禁用 p1
      const p1Node = registry.get('p1');
      p1Node && (p1Node.enabled = false);

      const next = registry.getNextAvailable('p1');
      expect(next?.provider.name).toBe('p2');
    });

    it('所有 provider 都不可用时返回 undefined', () => {
      registry.clearAllExhausted();
      registry.get('p1')!.enabled = false;
      registry.get('p2')!.enabled = false;
      registry.get('p3')!.enabled = false;

      const next = registry.getNextAvailable('p1');
      expect(next).toBeUndefined();
    });

    it('getPrimary 返回第一个可用 provider', () => {
      const primary = registry.getPrimary();
      expect(primary?.provider.name).toBe('p1');
    });
  });

  describe('熔断器集成', () => {
    beforeEach(() => {
      registry.register(createMockProvider('provider'), {
        breakerConfig: { failureThreshold: 3, cooldownMs: 1000 }
      });
    });

    it('Provider 节点包含熔断器', () => {
      const node = registry.get('provider');
      expect(node?.breaker).toBeInstanceOf(CircuitBreaker);
    });

    it('熔断后 provider 不再可用', async () => {
      const node = registry.get('provider');

      // 触发熔断
      for (let i = 0; i < 3; i++) {
        await node?.breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }

      expect(node?.breaker.isOpen()).toBe(true);
      expect(registry.listAvailable().length).toBe(0);
    });

    it('resetAllBreakers 重置所有熔断器', async () => {
      const node = registry.get('provider');

      for (let i = 0; i < 3; i++) {
        await node?.breaker.call(() => Promise.reject(new Error('fail'))).catch(() => {});
      }

      registry.resetAllBreakers();

      expect(node?.breaker.isOpen()).toBe(false);
      expect(registry.listAvailable().length).toBe(1);
    });
  });

  describe('健康检查', () => {
    beforeEach(() => {
      registry.register(createMockProvider('healthy'));
      registry.register(createMockProvider('unhealthy'));
    });

    it('healthCheckAll 检查所有 provider', async () => {
      const results = await registry.healthCheckAll(async (p) => {
        return p.name === 'healthy';
      });

      expect(results.get('healthy')).toBe(true);
      expect(results.get('unhealthy')).toBe(false);
    });

    it('健康检查失败触发熔断', async () => {
      const node = registry.get('healthy')!;

      await registry.healthCheckAll(async (p) => {
        if (p.name === 'healthy') {
          throw new Error('health check failed');
        }
        return true;
      });

      // 健康检查失败应该触发熔断
      expect(node.breaker.isOpen()).toBe(false); // 注意：healthCheckAll 内部不会自动熔断
    });
  });

  describe('状态摘要', () => {
    beforeEach(() => {
      registry.register(createMockProvider('p1', 'vendor1'));
      registry.register(createMockProvider('p2', 'vendor2'));
    });

    it('getStatus 返回完整状态', () => {
      const status = registry.getStatus();

      expect(status.length).toBe(2);
      expect(status[0]).toHaveProperty('name');
      expect(status[0]).toHaveProperty('vendor');
      expect(status[0]).toHaveProperty('enabled');
      expect(status[0]).toHaveProperty('exhausted');
      expect(status[0]).toHaveProperty('state');
    });
  });
});