/* eslint-disable @typescript-eslint/ban-ts-comment */
import { TestBed } from '@angular/core/testing';
import { AngularFireModule, FIREBASE_APP_NAME, FIREBASE_OPTIONS, FirebaseApp } from '@angular/fire/compat';
import { AngularFireAuth, AngularFireAuthModule, SETTINGS, USE_EMULATOR } from '@angular/fire/compat/auth';
import firebase from 'firebase/compat/app';
import { Observable, Subject } from 'rxjs';
import { COMMON_CONFIG, authEmulatorPort } from '../../../src/test-config';
import 'firebase/compat/auth';
import { rando } from '../../../src/utils';

const firebaseUser = {
  uid: '12345',
  providerData: [{ displayName: 'jeffbcrossyface' }]
} as firebase.User;

describe('AngularFireAuth', () => {
  let afAuth: AngularFireAuth;
  let mockAuthState: Subject<firebase.User>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
        AngularFireAuthModule
      ],
      providers: [
        { provide: SETTINGS, useValue: { appVerificationDisabledForTesting: true } },
        { provide: USE_EMULATOR, useValue: [`http://localhost:${authEmulatorPort}`] },
      ]
    });

    afAuth = TestBed.inject(AngularFireAuth);

    mockAuthState = new Subject<firebase.User>();
    // @ts-ignore
    spyOn(afAuth, 'authState').and.returnValue(mockAuthState);
    // @ts-ignore
    spyOn(afAuth, 'idToken').and.returnValue(mockAuthState);
    (afAuth as any).authState = mockAuthState as Observable<firebase.User>;
    (afAuth as any).idToken = mockAuthState as Observable<firebase.User>;
  });

  describe('Zones', () => {
    it('should call operators and subscriber in the same zone as when service was initialized', (done) => {
      // Initialize the app outside of the zone, to mimick real life behavior.
      const ngZone = Zone.current.fork({
        name: 'ngZone'
      });
      ngZone.run(() => {
        const subs = [
          afAuth.authState.subscribe(() => {
            expect(Zone.current.name).toBe('ngZone');
            done();
          }, done.fail),
          // afAuth.authState.subscribe(() => {
          //   expect(Zone.current.name).toBe('ngZone');
          //   done();
          // }, done.fail)
        ];
        mockAuthState.next(firebaseUser);
        subs.forEach(s => s.unsubscribe());
      });
    });
  });

  it('should exist', () => {
    expect(afAuth instanceof AngularFireAuth).toBe(true);
  });

  it('should have an initialized Firebase app', () => {
    expect(afAuth.app).toBeDefined();
  });

  it('should have disabled app verification for testing', async () => {
    const app = await afAuth.app;
    expect(app.auth().settings.appVerificationDisabledForTesting).toBe(true);
  });

  it('should emit auth updates through authState', (done: any) => {
    let count = 0;

    // Check that the first value is null and second is the auth user
    const subs = afAuth.authState.subscribe({
      next: (user => {
        if (count === 0) {
          expect(user).toBe(null);
          count = count + 1;
          mockAuthState.next(firebaseUser);
        } else {
          expect(user).toEqual(firebaseUser);
          subs.unsubscribe();
          done();
        }
      }),
      error: done,
      complete: done.fail
    });
    mockAuthState.next(null);
  });

  it('should emit auth updates through idToken', (done: any) => {
    let count = 0;

    // Check that the first value is null and second is the auth user
    const subs = afAuth.idToken.subscribe({
      next: user => {
        if (count === 0) {
          expect(user).toBe(null);
          count = count + 1;
          mockAuthState.next(firebaseUser);
        } else {
          expect(user as any).toEqual(firebaseUser);
          subs.unsubscribe();
          done();
        }
      },
      error: done,
      complete: done.fail
    });
    mockAuthState.next(null);
  });

});

describe('AngularFireAuth with different app', () => {
  let app: FirebaseApp;
  let afAuth: AngularFireAuth;
  let firebaseAppName: string;

  beforeEach(() => {
    firebaseAppName = rando();

    TestBed.configureTestingModule({
      imports: [
        AngularFireModule.initializeApp(COMMON_CONFIG, rando()),
        AngularFireAuthModule
      ],
      providers: [
        { provide: FIREBASE_APP_NAME, useValue: firebaseAppName },
        { provide: FIREBASE_OPTIONS, useValue: COMMON_CONFIG },
        { provide: USE_EMULATOR, useValue: [`http://localhost:${authEmulatorPort}`] },
      ]
    });
    app = TestBed.inject(FirebaseApp);
    afAuth = TestBed.inject(AngularFireAuth);
  });

  describe('<constructor>', () => {

    it('should be an AngularFireAuth type', () => {
      expect(afAuth instanceof AngularFireAuth).toEqual(true);
    });

    it('should have an initialized Firebase app', () => {
      expect(afAuth.app).toBeDefined();
    });

    it('should have an initialized Firebase app instance member', async () => {
      const itsApp = await afAuth.app;
      expect(itsApp).toEqual(app);
    });
  });

});
