const GameCenterAuth = require('../../../lib/Adapters/Auth/gcenter').default;
const { pki } = require('node-forge');
const fs = require('fs');
const path = require('path');

describe('GameCenterAuth Adapter', function () {
  let adapter;

  beforeEach(function () {
    adapter = new GameCenterAuth.constructor();

    const gcProd4 = fs.readFileSync(path.resolve(__dirname, '../../support/cert/gc-prod-4.cer'));
    const digicertPem = fs.readFileSync(path.resolve(__dirname, '../../support/cert/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem')).toString();

    mockFetch([
      {
        url: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        method: 'GET',
        response: {
          ok: true,
          headers: new Map(),
          arrayBuffer: () => Promise.resolve(
            gcProd4.buffer.slice(gcProd4.byteOffset, gcProd4.byteOffset + gcProd4.length)
          ),
        },
      },
      {
        url: 'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem',
        method: 'GET',
        response: {
          ok: true,
          headers: new Map([['content-type', 'application/x-pem-file'], ['content-length', digicertPem.length.toString()]]),
          text: () => Promise.resolve(digicertPem),
        },
      }
    ]);
  });

  describe('Test config failing due to missing params or wrong types', function () {
    it('should throw error for invalid options', async function () {
      const invalidOptions = [
        null,
        undefined,
        {},
        { bundleId: '' },
        { enableInsecureAuth: false }, // Missing bundleId in secure mode
      ];

      for (const options of invalidOptions) {
        expect(() => adapter.validateOptions(options)).withContext(JSON.stringify(options)).toThrow()
      }
    });

    it('should validate options successfully with valid parameters', function () {
      const validOptions = { bundleId: 'com.valid.app', enableInsecureAuth: false };
      expect(() => adapter.validateOptions(validOptions)).not.toThrow();
    });
  });

  describe('Test payload failing due to missing params or wrong types', function () {
    it('should throw error for missing authData fields', async function () {
      await expectAsync(adapter.validateAuthData({})).toBeRejectedWithError(
        'AuthData id is missing.'
      );
    });
  });

  describe('Test payload fails due to incorrect appId / certificate', function () {
    it('should throw error for invalid publicKeyUrl', async function () {
      const invalidPublicKeyUrl = 'https://malicious.url.com/key.cer';

      spyOn(adapter, 'fetchCertificate').and.throwError(
        new Error('Invalid publicKeyUrl')
      );

      await expectAsync(
        adapter.getAppleCertificate(invalidPublicKeyUrl)
      ).toBeRejectedWithError('Invalid publicKeyUrl: https://malicious.url.com/key.cer');
    });

    it('should throw error for invalid signature verification', async function () {
      const fakePublicKey = 'invalid-key';
      const fakeAuthData = {
        id: '1234567',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1460981421303,
        salt: 'saltST==',
        signature: 'invalidSignature',
      };

      spyOn(adapter, 'getAppleCertificate').and.returnValue(Promise.resolve(fakePublicKey));
      spyOn(adapter, 'verifySignature').and.throwError('Invalid signature.');

      await expectAsync(adapter.validateAuthData(fakeAuthData)).toBeRejectedWithError(
        'Invalid signature.'
      );
    });
  });

  describe('Test payload passing', function () {
    it('should successfully process valid payload and save auth data', async function () {
      const validAuthData = {
        id: '1234567',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1460981421303,
        salt: 'saltST==',
        signature: 'validSignature',
        bundleId: 'com.valid.app',
      };

      spyOn(adapter, 'getAppleCertificate').and.returnValue(Promise.resolve('validKey'));
      spyOn(adapter, 'verifySignature').and.returnValue(true);

      await expectAsync(adapter.validateAuthData(validAuthData)).toBeResolved();
    });
  });

  describe('Certificate and Signature Validation', function () {
    it('should fetch and validate Apple certificate', async function () {
      const certUrl = 'https://static.gc.apple.com/public-key/gc-prod-4.cer';
      const mockCertificate = 'mockCertificate';

      spyOn(adapter, 'fetchCertificate').and.returnValue(
        Promise.resolve({ certificate: mockCertificate, headers: new Map() })
      );
      spyOn(pki, 'certificateFromPem').and.returnValue({});

      adapter.cache[certUrl] = mockCertificate;

      const cert = await adapter.getAppleCertificate(certUrl);
      expect(cert).toBe(mockCertificate);
    });

    it('should verify signature successfully', async function () {
      const authData = {
        id: 'G:1965586982',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1565257031287,
        signature:
          'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
        salt: 'DzqqrQ==',
      };

      adapter.bundleId = 'cloud.xtralife.gamecenterauth';
      adapter.enableInsecureAuth = false;

      spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();

      const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);

      expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();

    });

    it('should not use bundle id from authData payload in secure mode', async function () {
      const authData = {
        id: 'G:1965586982',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1565257031287,
        signature:
          'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
        salt: 'DzqqrQ==',
        bundleId: 'com.example.insecure.app',
      };

      adapter.bundleId = 'cloud.xtralife.gamecenterauth';
      adapter.enableInsecureAuth = false;

      spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();

      const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);

      expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();

    });

    it('should not use bundle id from authData payload in insecure mode', async function () {
      const authData = {
        id: 'G:1965586982',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1565257031287,
        signature:
          'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
        salt: 'DzqqrQ==',
        bundleId: 'com.example.insecure.app',
      };

      adapter.bundleId = 'cloud.xtralife.gamecenterauth';
      adapter.enableInsecureAuth = true;

      spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();

      const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);

      expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();

    });

    it('can  use bundle id from authData payload in insecure mode', async function () {
      const authData = {
        id: 'G:1965586982',
        publicKeyUrl: 'https://static.gc.apple.com/public-key/gc-prod-4.cer',
        timestamp: 1565257031287,
        signature:
          'uqLBTr9Uex8zCpc1UQ1MIDMitb+HUat2Mah4Kw6AVLSGe0gGNJXlih2i5X+0ZwVY0S9zY2NHWi2gFjmhjt/4kxWGMkupqXX5H/qhE2m7hzox6lZJpH98ZEUbouWRfZX2ZhUlCkAX09oRNi7fI7mWL1/o88MaI/y6k6tLr14JTzmlxgdyhw+QRLxRPA6NuvUlRSJpyJ4aGtNH5/wHdKQWL8nUnFYiYmaY8R7IjzNxPfy8UJTUWmeZvMSgND4u8EjADPsz7ZtZyWAPi8kYcAb6M8k0jwLD3vrYCB8XXyO2RQb/FY2TM4zJuI7PzLlvvgOJXbbfVtHx7Evnm5NYoyzgzw==',
        salt: 'DzqqrQ==',
        bundleId: 'cloud.xtralife.gamecenterauth',
      };

      adapter.enableInsecureAuth = true;

      spyOn(adapter, 'verifyPublicKeyIssuer').and.returnValue();

      const publicKey = await adapter.getAppleCertificate(authData.publicKeyUrl);

      expect(() => adapter.verifySignature(publicKey, authData)).not.toThrow();

    });
  });
});
