import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { LoadingComponent, LoadingVariant, LoadingSize } from './loading';

describe('LoadingComponent', () => {
  let component: LoadingComponent;
  let fixture: ComponentFixture<LoadingComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [LoadingComponent],
    }).compileComponents();

    fixture = TestBed.createComponent(LoadingComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  describe('Input Properties', () => {
    it('should have default values', () => {
      expect(component.variant).toBe('spinner');
      expect(component.size).toBe('md');
      expect(component.animated).toBe(true);
      expect(component.label).toBe('Loading…');
    });

    it('should accept variant input', () => {
      const variants: LoadingVariant[] = ['spinner', 'bar', 'pulse'];
      variants.forEach(variant => {
        component.variant = variant;
        fixture.detectChanges();
        expect(component.variant).toBe(variant);
      });
    });

    it('should accept size input', () => {
      const sizes: LoadingSize[] = ['sm', 'md', 'lg'];
      sizes.forEach(size => {
        component.size = size;
        fixture.detectChanges();
        expect(component.size).toBe(size);
      });
    });

    it('should accept animated input', () => {
      component.animated = false;
      fixture.detectChanges();
      expect(component.animated).toBe(false);
    });

    it('should accept label input', () => {
      component.label = 'Loading data…';
      fixture.detectChanges();
      expect(component.label).toBe('Loading data…');
    });
  });

  describe('Template Rendering', () => {
    it('should render loading element', () => {
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement).toBeTruthy();
    });

    it('should apply variant class', () => {
      component.variant = 'bar';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--bar'));
      expect(loadingElement).toBeTruthy();
    });

    it('should apply size class', () => {
      component.size = 'lg';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--lg'));
      expect(loadingElement).toBeTruthy();
    });

    it('should apply static class when animated is false', () => {
      component.animated = false;
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--static'));
      expect(loadingElement).toBeTruthy();
    });

    it('should render spinner when variant is spinner', () => {
      component.variant = 'spinner';
      fixture.detectChanges();
      const spinnerElement = fixture.debugElement.query(By.css('.ccl-loading__spinner'));
      expect(spinnerElement).toBeTruthy();
    });

    it('should render bar when variant is bar', () => {
      component.variant = 'bar';
      fixture.detectChanges();
      const barElement = fixture.debugElement.query(By.css('.ccl-loading__bar-container'));
      expect(barElement).toBeTruthy();
    });

    it('should render pulse when variant is pulse', () => {
      component.variant = 'pulse';
      fixture.detectChanges();
      const pulseElement = fixture.debugElement.query(By.css('.ccl-loading__pulse'));
      expect(pulseElement).toBeTruthy();
    });

    it('should render three pulse dots', () => {
      component.variant = 'pulse';
      fixture.detectChanges();
      const pulseDots = fixture.debugElement.queryAll(By.css('.ccl-loading__pulse-dot'));
      expect(pulseDots.length).toBe(3);
    });

    it('should apply animated class to spinner when animated is true', () => {
      component.variant = 'spinner';
      component.animated = true;
      fixture.detectChanges();
      const spinnerElement = fixture.debugElement.query(By.css('.ccl-loading__spinner--animated'));
      expect(spinnerElement).toBeTruthy();
    });

    it('should not apply animated class to spinner when animated is false', () => {
      component.variant = 'spinner';
      component.animated = false;
      fixture.detectChanges();
      const spinnerElement = fixture.debugElement.query(By.css('.ccl-loading__spinner--animated'));
      expect(spinnerElement).toBeFalsy();
    });

    it('should apply animated class to bar when animated is true', () => {
      component.variant = 'bar';
      component.animated = true;
      fixture.detectChanges();
      const barElement = fixture.debugElement.query(By.css('.ccl-loading__bar--animated'));
      expect(barElement).toBeTruthy();
    });

    it('should apply animated class to pulse when animated is true', () => {
      component.variant = 'pulse';
      component.animated = true;
      fixture.detectChanges();
      const pulseElement = fixture.debugElement.query(By.css('.ccl-loading__pulse--animated'));
      expect(pulseElement).toBeTruthy();
    });
  });

  describe('Accessibility', () => {
    it('should have correct role', () => {
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement.nativeElement.getAttribute('role')).toBe('status');
    });

    it('should have correct aria-label', () => {
      component.label = 'Loading profile picture';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement.nativeElement.getAttribute('aria-label')).toBe('Loading profile picture');
    });

    it('should have default aria-label when no label provided', () => {
      component.label = '';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement.nativeElement.getAttribute('aria-label')).toBe('Loading…');
    });

    it('should update aria-label when label changes', () => {
      component.label = 'Initial label';
      fixture.detectChanges();
      
      let loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement.nativeElement.getAttribute('aria-label')).toBe('Initial label');
      
      component.label = 'Updated label';
      fixture.detectChanges();
      
      loadingElement = fixture.debugElement.query(By.css('.ccl-loading'));
      expect(loadingElement.nativeElement.getAttribute('aria-label')).toBe('Updated label');
    });
  });

  describe('CSS Classes Logic', () => {
    it('should generate correct CSS classes for default state', () => {
      const expectedClasses = 'ccl-loading ccl-loading--spinner ccl-loading--md';
      expect(component.cssClasses).toBe(expectedClasses);
    });

    it('should generate correct CSS classes for bar variant', () => {
      component.variant = 'bar';
      component.size = 'lg';
      const expectedClasses = 'ccl-loading ccl-loading--bar ccl-loading--lg';
      expect(component.cssClasses).toBe(expectedClasses);
    });

    it('should generate correct CSS classes with static state', () => {
      component.variant = 'pulse';
      component.animated = false;
      const expectedClasses = 'ccl-loading ccl-loading--pulse ccl-loading--md ccl-loading--static';
      expect(component.cssClasses).toBe(expectedClasses);
    });

    it('should generate correct spinner classes when animated', () => {
      component.variant = 'spinner';
      component.animated = true;
      expect(component.spinnerClasses).toBe('ccl-loading__spinner ccl-loading__spinner--animated');
    });

    it('should generate correct spinner classes when not animated', () => {
      component.variant = 'spinner';
      component.animated = false;
      expect(component.spinnerClasses).toBe('ccl-loading__spinner');
    });

    it('should generate correct bar classes when animated', () => {
      component.variant = 'bar';
      component.animated = true;
      expect(component.barClasses).toBe('ccl-loading__bar ccl-loading__bar--animated');
    });

    it('should generate correct pulse classes when animated', () => {
      component.variant = 'pulse';
      component.animated = true;
      expect(component.pulseClasses).toBe('ccl-loading__pulse ccl-loading__pulse--animated');
    });
  });

  describe('Variant Switching', () => {
    it('should switch from spinner to bar', () => {
      component.variant = 'spinner';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner'))).toBeTruthy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar-container'))).toBeFalsy();

      component.variant = 'bar';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner'))).toBeFalsy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar-container'))).toBeTruthy();
    });

    it('should switch from bar to pulse', () => {
      component.variant = 'bar';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar-container'))).toBeTruthy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse'))).toBeFalsy();

      component.variant = 'pulse';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar-container'))).toBeFalsy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse'))).toBeTruthy();
    });

    it('should switch from pulse to spinner', () => {
      component.variant = 'pulse';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse'))).toBeTruthy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner'))).toBeFalsy();

      component.variant = 'spinner';
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse'))).toBeFalsy();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner'))).toBeTruthy();
    });
  });

  describe('Animation Toggle', () => {
    it('should toggle animation on spinner', () => {
      component.variant = 'spinner';
      component.animated = true;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner--animated'))).toBeTruthy();

      component.animated = false;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__spinner--animated'))).toBeFalsy();
    });

    it('should toggle animation on bar', () => {
      component.variant = 'bar';
      component.animated = true;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar--animated'))).toBeTruthy();

      component.animated = false;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__bar--animated'))).toBeFalsy();
    });

    it('should toggle animation on pulse', () => {
      component.variant = 'pulse';
      component.animated = true;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse--animated'))).toBeTruthy();

      component.animated = false;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('.ccl-loading__pulse--animated'))).toBeFalsy();
    });
  });

  describe('Size Variants', () => {
    it('should apply small size class', () => {
      component.size = 'sm';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--sm'));
      expect(loadingElement).toBeTruthy();
    });

    it('should apply medium size class', () => {
      component.size = 'md';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--md'));
      expect(loadingElement).toBeTruthy();
    });

    it('should apply large size class', () => {
      component.size = 'lg';
      fixture.detectChanges();
      const loadingElement = fixture.debugElement.query(By.css('.ccl-loading--lg'));
      expect(loadingElement).toBeTruthy();
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty label gracefully', () => {
      component.label = '';
      fixture.detectChanges();
      expect(component.ariaLabel).toBe('Loading…');
    });

    it('should handle null/undefined label gracefully', () => {
      component.label = null as any;
      fixture.detectChanges();
      expect(component.ariaLabel).toBe('Loading…');
    });

    it('should maintain role when variant changes', () => {
      component.variant = 'spinner';
      fixture.detectChanges();
      expect(component.role).toBe('status');

      component.variant = 'bar';
      fixture.detectChanges();
      expect(component.role).toBe('status');

      component.variant = 'pulse';
      fixture.detectChanges();
      expect(component.role).toBe('status');
    });
  });
});
