import { VaultfireClient } from '../src/client';
import { VaultfireChain } from '../src/chains';

// Helper: retry with delay for flaky RPC calls
async function withRetry<T>(fn: () => Promise<T>, retries = 3, delayMs = 2000): Promise<T> {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (e: any) {
      if (i === retries - 1) throw e;
      await new Promise((r) => setTimeout(r, delayMs * (i + 1)));
    }
  }
  throw new Error('unreachable');
}

describe('VaultfireClient', () => {
  test('creates client with default config (base)', () => {
    const client = new VaultfireClient();
    expect(client.chain).toBe('base');
    expect(client.chainConfig.chainId).toBe(8453);
  });

  test('creates client for each chain', () => {
    const chains: VaultfireChain[] = ['base', 'avalanche', 'arbitrum', 'polygon'];
    for (const chain of chains) {
      const client = new VaultfireClient({ chain });
      expect(client.chain).toBe(chain);
    }
  });

  test('custom RPC URL overrides default', () => {
    const client = new VaultfireClient({
      chain: 'base',
      rpcUrl: 'https://custom-rpc.example.com',
    });
    expect(client.chain).toBe('base');
  });

  test('throws on getAddress without signer', async () => {
    const client = new VaultfireClient();
    await expect(client.getAddress()).rejects.toThrow('Signer required');
  });

  test('throws on getBalance without signer', async () => {
    const client = new VaultfireClient();
    await expect(client.getBalance()).rejects.toThrow('Signer required');
  });

  test('throws on registerAgent without signer', async () => {
    const client = new VaultfireClient();
    await expect(
      client.registerAgent('https://example.com', 'test', ['test'])
    ).rejects.toThrow('Signer required');
  });

  test('throws on createBond without signer', async () => {
    const client = new VaultfireClient();
    await expect(
      client.createBond('0x0000000000000000000000000000000000000001', 'collaboration')
    ).rejects.toThrow('Signer required');
  });
});

describe('VaultfireClient — Live Reads (Base)', () => {
  const client = new VaultfireClient({ chain: 'base' });
  const DEPLOYER = '0xA054f831B562e729F8D268291EBde1B2EDcFb84F';

  test('getTotalAgents returns a number > 0', async () => {
    const total = await withRetry(() => client.getTotalAgents());
    expect(total).toBeGreaterThan(0);
  }, 30000);

  test('isAgentActive returns true for deployer on Base', async () => {
    const active = await withRetry(() => client.isAgentActive(DEPLOYER));
    expect(active).toBe(true);
  }, 30000);

  test('getAgent returns valid data for deployer', async () => {
    const agent = await withRetry(() => client.getAgent(DEPLOYER));
    expect(agent.address).toBe(DEPLOYER);
    expect(agent.active).toBe(true);
    expect(agent.chain).toBe('base');
  }, 30000);

  test('getStreetCred returns score >= 30 for deployer (has identity)', async () => {
    const cred = await withRetry(() => client.getStreetCred(DEPLOYER));
    expect(cred.score).toBeGreaterThanOrEqual(30);
    expect(cred.breakdown.identityRegistered).toBe(30);
    expect(['bronze', 'silver', 'gold', 'platinum']).toContain(cred.tier);
  }, 45000);

  test('getReputation returns valid structure', async () => {
    const rep = await withRetry(() => client.getReputation(DEPLOYER));
    expect(rep.address).toBe(DEPLOYER);
    expect(typeof rep.averageRating).toBe('number');
    expect(typeof rep.totalFeedbacks).toBe('number');
  }, 30000);

  test('verifyTrust returns complete trust profile', async () => {
    const trust = await withRetry(() => client.verifyTrust(DEPLOYER));
    expect(trust.address).toBe(DEPLOYER);
    expect(trust.isActive).toBe(true);
    expect(trust.trusted).toBe(true);
    expect(trust.streetCred.score).toBeGreaterThanOrEqual(30);
    expect(trust.chain).toBe('base');
    expect(trust.reason).toContain('Street Cred');
  }, 60000);
});

describe('VaultfireClient — Live Reads (Arbitrum)', () => {
  const client = new VaultfireClient({ chain: 'arbitrum' });
  const DEPLOYER = '0xA054f831B562e729F8D268291EBde1B2EDcFb84F';

  test('getTotalAgents returns a number >= 1', async () => {
    const total = await withRetry(() => client.getTotalAgents());
    expect(total).toBeGreaterThanOrEqual(1);
  }, 30000);

  test('deployer is active on Arbitrum', async () => {
    const active = await withRetry(() => client.isAgentActive(DEPLOYER));
    expect(active).toBe(true);
  }, 30000);

  test('getStreetCred works on Arbitrum', async () => {
    const cred = await withRetry(() => client.getStreetCred(DEPLOYER));
    expect(cred.score).toBeGreaterThanOrEqual(30);
    expect(cred.chain).toBe('arbitrum');
  }, 45000);
});

describe('VaultfireClient — Live Reads (Polygon)', () => {
  const client = new VaultfireClient({ chain: 'polygon' });
  const DEPLOYER = '0xA054f831B562e729F8D268291EBde1B2EDcFb84F';

  test('deployer is active on Polygon', async () => {
    const active = await withRetry(() => client.isAgentActive(DEPLOYER));
    expect(active).toBe(true);
  }, 30000);
});

describe('VaultfireClient — Live Reads (Avalanche)', () => {
  const client = new VaultfireClient({ chain: 'avalanche' });
  const DEPLOYER = '0xA054f831B562e729F8D268291EBde1B2EDcFb84F';

  test('deployer is active on Avalanche', async () => {
    const active = await withRetry(() => client.isAgentActive(DEPLOYER));
    expect(active).toBe(true);
  }, 30000);
});
