import Client from '../dist/node/mina-signer/mina-signer.js';
import type { Keypair } from '../dist/node/mina-signer/src/types.js';

describe('Stake Delegation', () => {
  describe('Mainnet network', () => {
    let client: Client;
    let keypair: Keypair;

    beforeAll(async () => {
      client = new Client({ network: 'mainnet' });
      keypair = client.genKeys();
    });

    it('generates a signed staked delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(delegation.data).toBeDefined();
      expect(delegation.signature).toBeDefined();
    });

    it('generates a signed staked delegation using signTransaction', () => {
      const delegation = client.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(delegation.data).toBeDefined();
      expect(delegation.signature).toBeDefined();
    });

    it('verifies a signed delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDelegation = client.verifyStakeDelegation(delegation);
      expect(verifiedDelegation).toBeTruthy();
      expect(client.verifyTransaction(delegation)).toEqual(true);
    });

    it('verifies a signed delegation generated by signTransaction', () => {
      const delegation = client.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDelegation = client.verifyStakeDelegation(delegation);
      expect(verifiedDelegation).toBeTruthy();
      expect(client.verifyTransaction(delegation)).toEqual(true);
    });

    it('hashes a signed stake delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const hashedDelegation = client.hashStakeDelegation(delegation);
      expect(hashedDelegation).toBeDefined();
    });

    it('hashes a signed stake delegation generated by signTransaction', () => {
      const delegation = client.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const hashedDelegation = client.hashStakeDelegation(delegation);
      expect(hashedDelegation).toBeDefined();
    });

    it('does not verify a signed message from `testnet`', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const testnetClient = new Client({ network: 'testnet' });
      const invalidMessage = testnetClient.verifyStakeDelegation(delegation);
      expect(invalidMessage).toBeFalsy();
      expect(testnetClient.verifyTransaction(delegation)).toEqual(false);
    });
  });

  describe('Devnet network', () => {
    let client: Client;
    let keypair: Keypair;

    beforeAll(async () => {
      client = new Client({ network: 'testnet' });
      keypair = client.genKeys();
    });

    it('generates a signed staked delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(delegation.data).toBeDefined();
      expect(delegation.signature).toBeDefined();
    });

    it('generates a signed staked delegation using signTransaction', () => {
      const delegation = client.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(delegation.data).toBeDefined();
      expect(delegation.signature).toBeDefined();
    });

    it('verifies a signed delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDelegation = client.verifyStakeDelegation(delegation);
      expect(verifiedDelegation).toBeTruthy();
      expect(client.verifyTransaction(delegation)).toEqual(true);
    });

    it('verifies a signed delegation generated by signTransaction', () => {
      const delegation = client.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDelegation = client.verifyStakeDelegation(delegation);
      expect(verifiedDelegation).toBeTruthy();
      expect(client.verifyTransaction(delegation)).toEqual(true);
    });

    it('hashes a signed stake delegation', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const hashedDelegation = client.hashStakeDelegation(delegation);
      expect(hashedDelegation).toBeDefined();
    });

    it('does not verify a signed message from `mainnet`', () => {
      const delegation = client.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const mainnetClient = new Client({ network: 'mainnet' });
      const invalidMessage = mainnetClient.verifyStakeDelegation(delegation);
      expect(invalidMessage).toBeFalsy();
      expect(mainnetClient.verifyTransaction(delegation)).toEqual(false);
    });
  });
  describe('Testnet network', () => {
    let testnetClient: Client;
    let devnetClient: Client;
    let keypair: Keypair;

    beforeAll(async () => {
      testnetClient = new Client({ network: 'testnet' });
      devnetClient = new Client({ network: 'devnet' });
      keypair = testnetClient.genKeys();
    });

    it('generates the same signed stake delegation as devnet', () => {
      const testnetDelegation = testnetClient.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const devnetDelegation = testnetClient.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(testnetDelegation).toEqual(devnetDelegation);
    });
    it('generates the same signed stake delegation as devnet using signTransaction', () => {
      const testnetDelegation = testnetClient.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const devnetDelegation = testnetClient.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      expect(testnetDelegation).toEqual(devnetDelegation);
    });

    it('verifies a signed delegation from devnet and vice versa', () => {
      const testnetDelegation = testnetClient.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const devnetDelegation = testnetClient.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDevnetDelegation = testnetClient.verifyStakeDelegation(devnetDelegation);
      expect(verifiedDevnetDelegation).toBeTruthy();
      expect(testnetClient.verifyTransaction(devnetDelegation)).toEqual(true);

      const verifiedTestnetDelegation = devnetClient.verifyStakeDelegation(testnetDelegation);
      expect(verifiedTestnetDelegation).toBeTruthy();
      expect(devnetClient.verifyTransaction(testnetDelegation)).toEqual(true);
    });

    it('verifies a signed delegation generated by signTransaction from devnet and vice versa', () => {
      const testnetDelegation = testnetClient.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const devnetDelegation = testnetClient.signTransaction(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const verifiedDevnetDelegation = testnetClient.verifyStakeDelegation(devnetDelegation);
      expect(verifiedDevnetDelegation).toBeTruthy();
      expect(testnetClient.verifyTransaction(devnetDelegation)).toEqual(true);

      const verifiedTestnetDelegation = devnetClient.verifyStakeDelegation(testnetDelegation);
      expect(verifiedTestnetDelegation).toBeTruthy();
      expect(devnetClient.verifyTransaction(testnetDelegation)).toEqual(true);
    });

    it('does not verify a signed message from `mainnet`', () => {
      const delegation = testnetClient.signStakeDelegation(
        {
          to: keypair.publicKey,
          from: keypair.publicKey,
          fee: '1',
          nonce: '0',
        },
        keypair.privateKey
      );
      const mainnetClient = new Client({ network: 'mainnet' });
      const invalidMessage = mainnetClient.verifyStakeDelegation(delegation);
      expect(invalidMessage).toBeFalsy();
      expect(mainnetClient.verifyTransaction(delegation)).toEqual(false);
    });
  });
});
