import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputComponent } from './input';
import { By } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';

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

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

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

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

  describe('Input Properties', () => {
    it('should have default values', () => {
      expect(component.type).toBe('text');
      expect(component.label).toBe('');
      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('');
    });

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

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

    it('should accept placeholder input', () => {
      component.placeholder = 'Enter your email';
      fixture.detectChanges();
      expect(component.placeholder).toBe('Enter your email');
    });

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

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

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

    it('should accept errorMessage input', () => {
      component.errorMessage = 'Invalid email format';
      fixture.detectChanges();
      expect(component.errorMessage).toBe('Invalid email format');
    });

    it('should accept helperText input', () => {
      component.helperText = 'Must be a valid email';
      fixture.detectChanges();
      expect(component.helperText).toBe('Must be a valid email');
    });
  });

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

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

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

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

    it('should render label when provided', () => {
      component.label = 'Email';
      fixture.detectChanges();
      const label = fixture.debugElement.query(By.css('label'));
      expect(label.nativeElement.textContent).toContain('Email');
    });

    it('should not render label when not provided', () => {
      component.label = '';
      fixture.detectChanges();
      const label = fixture.debugElement.query(By.css('label'));
      expect(label).toBeNull();
    });

    it('should set input type', () => {
      component.type = 'email';
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      expect(input.nativeElement.type).toBe('email');
    });

    it('should set placeholder', () => {
      component.placeholder = 'Enter your email';
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      expect(input.nativeElement.placeholder).toBe('Enter your email');
    });

    it('should set min and max attributes', () => {
      component.type = 'number';
      component.min = 1;
      component.max = 100;
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      expect(input.nativeElement.getAttribute('min')).toBe('1');
      expect(input.nativeElement.getAttribute('max')).toBe('100');
    });

    it('should set input id', () => {
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      expect(input.nativeElement.id).toBe(component.id);
    });

    it('should set label for attribute', () => {
      component.label = 'Email';
      fixture.detectChanges();
      const label = fixture.debugElement.query(By.css('label'));
      expect(label.nativeElement.getAttribute('for')).toBe(component.id);
    });
  });

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

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

    it('should apply disabled CSS class when disabled=true', () => {
      component.disabled = true;
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      // The input doesn't have a disabled CSS class, it just has the disabled attribute
      expect(input.nativeElement.disabled).toBeTrue();
    });
  });

  describe('Readonly State', () => {
    it('should set readonly when readonly=true', () => {
      component.readonly = true;
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      expect(input.nativeElement.readOnly).toBeTrue();
    });

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

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

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

    it('should apply error CSS class when error=true', () => {
      component.error = true;
      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('input'));
      // The input doesn't have an error CSS class, it just has aria-invalid
      expect(input.nativeElement.getAttribute('aria-invalid')).toBe('true');
    });

    it('should display error message when provided', () => {
      component.error = true;
      component.errorMessage = 'Invalid email format';
      fixture.detectChanges();
      const errorMessage = fixture.debugElement.query(By.css('.ccl-input__error'));
      expect(errorMessage.nativeElement.textContent).toContain('Invalid email format');
    });

    it('should not display error message when error=false', () => {
      component.error = false;
      component.errorMessage = 'Invalid email format';
      fixture.detectChanges();
      const errorMessage = fixture.debugElement.query(By.css('.ccl-input__error'));
      expect(errorMessage).toBeNull();
    });
  });

  describe('Helper Text', () => {
    it('should render helper text when provided', () => {
      component.helperText = 'Must be a valid email';
      fixture.detectChanges();
      const helper = fixture.debugElement.query(By.css('.ccl-input__helper'));
      expect(helper.nativeElement.textContent).toContain('Must be a valid email');
    });

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

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

    it('should associate label with input', () => {
      component.label = 'Email';
      fixture.detectChanges();
      
      const label = fixture.debugElement.query(By.css('label'));
      const input = fixture.debugElement.query(By.css('input'));
      expect(label.nativeElement.getAttribute('for')).toBe(input.nativeElement.id);
    });
  });

  describe('ControlValueAccessor', () => {
    it('should implement ControlValueAccessor', () => {
      expect(component.writeValue).toBeDefined();
      expect(component.registerOnChange).toBeDefined();
      expect(component.registerOnTouched).toBeDefined();
      expect(component.setDisabledState).toBeDefined();
    });

    it('should write value to input', () => {
      component.writeValue('test value');
      expect(component.value).toBe('test value');
    });

    it('should register onChange callback', () => {
      const onChangeSpy = jasmine.createSpy('onChange');
      component.registerOnChange(onChangeSpy);
      
      const input = fixture.debugElement.query(By.css('input'));
      input.nativeElement.value = 'new value';
      input.nativeElement.dispatchEvent(new Event('input'));
      
      expect(onChangeSpy).toHaveBeenCalledWith('new value');
    });

    it('should register onTouched callback', () => {
      const onTouchedSpy = jasmine.createSpy('onTouched');
      component.registerOnTouched(onTouchedSpy);
      
      const input = fixture.debugElement.query(By.css('input'));
      input.nativeElement.dispatchEvent(new Event('input'));
      
      expect(onTouchedSpy).toHaveBeenCalled();
    });

    it('should set disabled state', () => {
      component.setDisabledState(true);
      expect(component.disabled).toBe(true);
    });

    it('should emit valueChange event', () => {
      spyOn(component.valueChange, 'emit');
      
      const input = fixture.debugElement.query(By.css('input'));
      input.nativeElement.value = 'test';
      input.nativeElement.dispatchEvent(new Event('input'));
      
      expect(component.valueChange.emit).toHaveBeenCalledWith('test');
    });
  });
});
