import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { SelectComponent, SelectOption } from './select';

@Component({
  template: `
    <ccl-select 
      [options]="options" 
      [placeholder]="placeholder"
      [disabled]="disabled"
      [multiple]="multiple"
      [size]="size"
      [error]="error"
      [errorMessage]="errorMessage"
      [helperText]="helperText"
      (selectionChange)="onSelectionChange($event)">
    </ccl-select>
  `,
  standalone: true,
  imports: [SelectComponent]
})
class TestHostComponent {
  options: SelectOption[] = [
    { value: 'us', label: 'United States' },
    { value: 'ca', label: 'Canada' },
    { value: 'mx', label: 'Mexico' }
  ];
  placeholder = '';
  disabled = false;
  multiple = false;
  size?: number;
  error = false;
  errorMessage = '';
  helperText = '';
  
  selectedValue: string | string[] = '';
  
  onSelectionChange(value: string | string[]): void {
    this.selectedValue = value;
  }
}

describe('SelectComponent', () => {
  let component: SelectComponent;
  let fixture: ComponentFixture<SelectComponent>;
  let hostComponent: TestHostComponent;
  let hostFixture: ComponentFixture<TestHostComponent>;

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

    fixture = TestBed.createComponent(SelectComponent);
    component = fixture.componentInstance;
    
    hostFixture = TestBed.createComponent(TestHostComponent);
    hostComponent = hostFixture.componentInstance;
    
    fixture.detectChanges();
    hostFixture.detectChanges();
  });

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

  describe('Input Properties', () => {
    it('should have default values', () => {
      expect(component.options).toEqual([]);
      expect(component.placeholder).toBe('');
      expect(component.disabled).toBe(false);
      expect(component.readonly).toBe(false);
      expect(component.error).toBe(false);
      expect(component.errorMessage).toBe('');
      expect(component.helperText).toBe('');
      expect(component.multiple).toBe(false);
      expect(component.size).toBeUndefined();
    });

    it('should accept options input', () => {
      const options: SelectOption[] = [
        { value: 'option1', label: 'Option 1' },
        { value: 'option2', label: 'Option 2' }
      ];
      component.options = options;
      fixture.detectChanges();
      expect(component.options).toEqual(options);
    });

    it('should accept placeholder input', () => {
      component.placeholder = 'Choose an option';
      fixture.detectChanges();
      expect(component.placeholder).toBe('Choose an option');
    });

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

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

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

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

    it('should accept errorMessage input', () => {
      component.errorMessage = 'Please select an option';
      fixture.detectChanges();
      expect(component.errorMessage).toBe('Please select an option');
    });

    it('should accept helperText input', () => {
      component.helperText = 'Select your country';
      fixture.detectChanges();
      expect(component.helperText).toBe('Select your country');
    });
  });

  describe('ID Generation', () => {
    it('should generate unique ID', () => {
      expect(component.id).toMatch(/^ccl-select-/);
      expect(component.id.length).toBeGreaterThan(10);
    });

    it('should have different IDs for different instances', () => {
      const fixture2 = TestBed.createComponent(SelectComponent);
      const component2 = fixture2.componentInstance;
      expect(component.id).not.toBe(component2.id);
    });
  });

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

    it('should apply base CSS class', () => {
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.classList).toContain('ccl-select');
    });

    it('should render options when provided', () => {
      component.options = [
        { value: 'us', label: 'United States' },
        { value: 'ca', label: 'Canada' }
      ];
      fixture.detectChanges();
      const options = fixture.debugElement.queryAll(By.css('option'));
      expect(options.length).toBe(2);
    });

    it('should render placeholder option when provided and not multiple', () => {
      component.placeholder = 'Choose...';
      component.multiple = false;
      fixture.detectChanges();
      const placeholderOption = fixture.debugElement.query(By.css('option[disabled]'));
      expect(placeholderOption.nativeElement.textContent).toContain('Choose...');
    });

    it('should not render placeholder option when multiple is true', () => {
      component.placeholder = 'Choose...';
      component.multiple = true;
      fixture.detectChanges();
      const placeholderOption = fixture.debugElement.query(By.css('option[disabled]'));
      expect(placeholderOption).toBeNull();
    });

    it('should render helper text when provided', () => {
      component.helperText = 'Select your country';
      fixture.detectChanges();
      const helper = fixture.debugElement.query(By.css('.ccl-select__helper'));
      expect(helper.nativeElement.textContent).toContain('Select your country');
    });

    it('should not render helper text when not provided', () => {
      component.helperText = '';
      fixture.detectChanges();
      const helper = fixture.debugElement.query(By.css('.ccl-select__helper'));
      expect(helper).toBeNull();
    });

    it('should render error message when provided', () => {
      component.error = true;
      component.errorMessage = 'Please select an option';
      fixture.detectChanges();
      const error = fixture.debugElement.query(By.css('.ccl-select__error'));
      expect(error.nativeElement.textContent).toContain('Please select an option');
    });

    it('should not render error message when error is false', () => {
      component.error = false;
      component.errorMessage = 'Please select an option';
      fixture.detectChanges();
      const error = fixture.debugElement.query(By.css('.ccl-select__error'));
      expect(error).toBeNull();
    });
  });

  describe('Disabled State', () => {
    it('should disable select when disabled=true', () => {
      component.disabled = true;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.disabled).toBeTrue();
    });

    it('should not disable select when disabled=false', () => {
      component.disabled = false;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.disabled).toBeFalse();
    });
  });

  describe('Multiple Selection', () => {
    it('should set multiple attribute when multiple=true', () => {
      component.multiple = true;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.multiple).toBeTrue();
    });

    it('should not set multiple attribute when multiple=false', () => {
      component.multiple = false;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.multiple).toBeFalse();
    });

    it('should set size attribute when size is provided', () => {
      component.size = 5;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.size).toBe(5);
    });
  });

  describe('Error State', () => {
    it('should set aria-invalid when error=true', () => {
      component.error = true;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.getAttribute('aria-invalid')).toBe('true');
    });

    it('should not set aria-invalid when error=false', () => {
      component.error = false;
      fixture.detectChanges();
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.getAttribute('aria-invalid')).toBe('false');
    });
  });

  describe('Selection Change', () => {
    it('should emit selection change for single selection', () => {
      spyOn(component.selectionChange, 'emit');
      component.options = [
        { value: 'us', label: 'United States' },
        { value: 'ca', label: 'Canada' }
      ];
      fixture.detectChanges();
      
      const select = fixture.debugElement.query(By.css('select'));
      select.nativeElement.value = 'us';
      select.nativeElement.dispatchEvent(new Event('change'));
      
      expect(component.selectionChange.emit).toHaveBeenCalledWith('us');
    });

    it('should emit selection change for multiple selection', () => {
      spyOn(component.selectionChange, 'emit');
      component.multiple = true;
      component.options = [
        { value: 'us', label: 'United States' },
        { value: 'ca', label: 'Canada' },
        { value: 'mx', label: 'Mexico' }
      ];
      fixture.detectChanges();
      
      const select = fixture.debugElement.query(By.css('select'));
      // Select options 1 and 2 (Canada and Mexico)
      select.nativeElement.options[1].selected = true;
      select.nativeElement.options[2].selected = true;
      select.nativeElement.dispatchEvent(new Event('change'));
      
      expect(component.selectionChange.emit).toHaveBeenCalledWith(['ca', 'mx']);
    });
  });

  describe('isSelected Method', () => {
    it('should return true for selected single value', () => {
      component.selectedValue = 'us';
      expect(component.isSelected('us')).toBeTrue();
    });

    it('should return false for non-selected single value', () => {
      component.selectedValue = 'us';
      expect(component.isSelected('ca')).toBeFalse();
    });

    it('should return true for selected multiple value', () => {
      component.multiple = true;
      component.selectedValue = ['us', 'ca'];
      expect(component.isSelected('us')).toBeTrue();
      expect(component.isSelected('ca')).toBeTrue();
    });

    it('should return false for non-selected multiple value', () => {
      component.multiple = true;
      component.selectedValue = ['us', 'ca'];
      expect(component.isSelected('mx')).toBeFalse();
    });
  });

  describe('Accessibility', () => {
    it('should have proper ARIA attributes', () => {
      component.error = true;
      component.helperText = 'Helper text';
      fixture.detectChanges();
      
      const select = fixture.debugElement.query(By.css('select'));
      expect(select.nativeElement.getAttribute('aria-invalid')).toBe('true');
      expect(select.nativeElement.getAttribute('aria-describedby')).toContain('help');
    });

    it('should associate helper text with select', () => {
      component.helperText = 'Select your country';
      fixture.detectChanges();
      
      const select = fixture.debugElement.query(By.css('select'));
      const helper = fixture.debugElement.query(By.css('.ccl-select__helper'));
      expect(select.nativeElement.getAttribute('aria-describedby')).toBe(helper.nativeElement.id);
    });
  });

  describe('Option Properties', () => {
    it('should handle disabled options', () => {
      component.options = [
        { value: 'us', label: 'United States' },
        { value: 'ca', label: 'Canada', disabled: true }
      ];
      fixture.detectChanges();
      
      const options = fixture.debugElement.queryAll(By.css('option'));
      expect(options[1].nativeElement.disabled).toBeTrue();
    });

    it('should handle option labels correctly', () => {
      component.options = [
        { value: 'us', label: 'United States' },
        { value: 'ca', label: 'Canada' }
      ];
      fixture.detectChanges();
      
      const options = fixture.debugElement.queryAll(By.css('option'));
      expect(options[0].nativeElement.textContent).toContain('United States');
      expect(options[1].nativeElement.textContent).toContain('Canada');
    });
  });
});
