import { CenterBottomDuringTransitionDirective } from './center-bottom-transition.directive';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { ElementRef, Renderer2, Component, ViewChild, DebugElement, Type } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component({
  template: `<div centerBottomDuringTransition className="test-class" [inTransition]="inTransition">
    Mock Component
  </div>`,
})
class TestHostComponent {
  @ViewChild(CenterBottomDuringTransitionDirective, { static: true })
  directive: CenterBottomDuringTransitionDirective;
  inTransition = false;

  setTransition(isTransitioning: boolean) {
    this.inTransition = isTransitioning;
  }
}

class MockElementRef {
  get nativeElement() {
    return {};
  }
}

fdescribe('Directive: CenterBottomTransition', () => {
  let fixture: ComponentFixture<TestHostComponent>;
  let component: TestHostComponent;
  let directive: CenterBottomDuringTransitionDirective;
  let elementRef: ElementRef;
  let renderer: Renderer2;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      providers: [Renderer2, { provide: ElementRef, useClass: MockElementRef }],
      declarations: [TestHostComponent, CenterBottomDuringTransitionDirective],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(TestHostComponent);
    component = fixture.componentInstance;
    directive = component.directive;
    elementRef = TestBed.inject(ElementRef);
    renderer = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);

    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(directive instanceof CenterBottomDuringTransitionDirective).toBeTruthy();
  });

  describe('applyPosition', () => {
    let mockElement: DebugElement;
    beforeEach(() => {
      spyOn(directive, 'applyPosition').and.callThrough();
      spyOn(renderer, 'addClass').and.callFake(() => {});
      spyOn(renderer, 'removeClass').and.callFake(() => {});

      mockElement = fixture.debugElement.query(By.css('div'));

      fixture.detectChanges();
    });

    it('should be called when inTransition Input changes', () => {
      component.inTransition = true;

      fixture.detectChanges();

      expect(directive.applyPosition).toHaveBeenCalledWith(true);
    });

    it('should return if nativeElement is undefined', () => {
      spyOn(elementRef, 'nativeElement').and.returnValue(undefined);

      directive.applyPosition(true);
    });

    it('should add the provided class to the native element if inTransition is true', () => {
      spyOn(elementRef, 'nativeElement').and.returnValue(mockElement.nativeElement);

      directive.applyPosition(true);

      expect(renderer.addClass).toHaveBeenCalledWith(mockElement.nativeElement, 'test-class');
      expect(renderer.removeClass).not.toHaveBeenCalled();
    });

    it('should remove the provided class from the native element if inTransition is false', () => {
      spyOn(elementRef, 'nativeElement').and.returnValue(mockElement.nativeElement);

      directive.applyPosition(false);

      expect(renderer.removeClass).toHaveBeenCalledWith(mockElement.nativeElement, 'test-class');
      expect(renderer.addClass).not.toHaveBeenCalled();
    });
  });
});
