import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { StatefulButtonComponent } from './stateful-button.component';
import { fakeAsync, tick } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

describe('StatefulButtonComponent', () => {
  let spectator: Spectator<StatefulButtonComponent>;
  const createComponent = createComponentFactory({
    component: StatefulButtonComponent,
    imports: [NoopAnimationsModule],
  });

  beforeEach(() => {
    spectator = createComponent({
      props: {
        defaultContent: 'Submit',
        successContent: 'Submitted',
        errorContent: 'Failed to submit',
      },
    });
  });

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

  it('should display default content initially', () => {
    expect(spectator.query('.stateful-button-content--default')).toHaveText('Submit');
    expect(spectator.component.state()).toBe('default');
  });

  it('should have correct e2e attributes', () => {
    const button = spectator.query('ui-button');
    expect(button).toHaveAttribute('data-what', 'stateful-button');
    expect(button).toHaveAttribute('data-which', 'default');
  });

  it('should apply correct CSS classes based on state', () => {
    expect(spectator.query('.stateful-button-content--default')).toExist();
    
    spectator.component.state.set('success');
    spectator.detectChanges();
    expect(spectator.query('.stateful-button-content--success')).toExist();
    
    spectator.component.state.set('error');
    spectator.detectChanges();
    expect(spectator.query('.stateful-button-content--error')).toExist();
  });

  it('should emit clicked event when clicked in default state', () => {
    const clickedSpy = jest.fn();
    spectator.output('clicked').subscribe(clickedSpy);

    spectator.click('ui-button');

    expect(clickedSpy).toHaveBeenCalledTimes(1);
  });

  it('should transition from success to default when clicked', () => {
    spectator.component.state.set('success');
    spectator.detectChanges();

    expect(spectator.component.state()).toBe('success');
    expect(spectator.query('.stateful-button-content--success')).toContainText('Submitted');

    spectator.click('ui-button');

    expect(spectator.component.state()).toBe('default');
  });

  it('should transition from error to default when clicked', () => {
    spectator.component.state.set('error');
    spectator.detectChanges();

    expect(spectator.component.state()).toBe('error');

    spectator.click('ui-button');

    expect(spectator.component.state()).toBe('default');
  });

  it('should display success icon and content', () => {
    spectator.component.state.set('success');
    spectator.detectChanges();

    expect(spectator.query('ng-icon')).toExist();
    expect(spectator.query('ng-icon')).toHaveAttribute('name', 'isaActionCheck');
    expect(spectator.query('.stateful-button-content--success')).toContainText('Submitted');
  });

  it('should display error content with action button', () => {
    spectator.setInput('errorAction', 'Try again');
    spectator.component.state.set('error');
    spectator.detectChanges();

    expect(spectator.query('.stateful-button-content--error')).toContainText('Failed to submit');
    expect(spectator.query('.stateful-button-action')).toHaveText('Try again');
  });

  it('should emit action event when error action is clicked', () => {
    spectator.setInput('errorAction', 'Try again');
    spectator.component.state.set('error');
    spectator.detectChanges();

    const actionSpy = jest.fn();
    spectator.output('action').subscribe(actionSpy);

    spectator.click('.stateful-button-action');

    expect(actionSpy).toHaveBeenCalledTimes(1);
  });

  it('should display error content without action when errorAction is not provided', () => {
    spectator.component.state.set('error');
    spectator.detectChanges();

    expect(spectator.query('.stateful-button-content--error')).toContainText('Failed to submit');
    expect(spectator.query('.stateful-button-action')).not.toExist();
  });

  it('should pass through button properties correctly', () => {
    spectator.setInput('color', 'brand');
    spectator.setInput('size', 'large');
    spectator.setInput('pending', true);
    spectator.detectChanges();

    const button = spectator.query('ui-button');
    expect(button).toHaveClass('ui-button__brand');
    expect(button).toHaveClass('ui-button__large');
    expect(button).toHaveClass('ui-button__pending');
  });

  it('should auto-dismiss success state after timeout', fakeAsync(() => {
    spectator.setInput('dismiss', 1000);
    spectator.component.state.set('success');
    spectator.detectChanges();

    expect(spectator.component.state()).toBe('success');

    tick(1000);

    expect(spectator.component.state()).toBe('default');
  }));

  it('should use correct widths for each state', () => {
    // Setup the expected widths
    spectator.setInput('defaultWidth', '10rem');
    spectator.setInput('successWidth', '20.375rem');
    spectator.setInput('errorWidth', '32rem');

    expect(spectator.component.widthStyle()).toBe('10rem');

    spectator.component.state.set('success');
    expect(spectator.component.widthStyle()).toBe('20.375rem');

    spectator.component.state.set('error');
    expect(spectator.component.widthStyle()).toBe('32rem');
  });

  it('should use custom widths when provided', () => {
    spectator.setInput('defaultWidth', '8rem');
    spectator.setInput('successWidth', '16rem');
    spectator.setInput('errorWidth', '24rem');

    expect(spectator.component.widthStyle()).toBe('8rem');

    spectator.component.state.set('success');
    expect(spectator.component.widthStyle()).toBe('16rem');

    spectator.component.state.set('error');
    expect(spectator.component.widthStyle()).toBe('24rem');
  });

  it('should update e2e data-which attribute based on state', () => {
    const button = spectator.query('ui-button');
    
    expect(button).toHaveAttribute('data-which', 'default');
    
    spectator.component.state.set('success');
    spectator.detectChanges();
    expect(button).toHaveAttribute('data-which', 'success');
    
    spectator.component.state.set('error');
    spectator.detectChanges();
    expect(button).toHaveAttribute('data-which', 'error');
  });

  it('should handle keyboard accessibility on action button', () => {
    spectator.setInput('errorAction', 'Try again');
    spectator.component.state.set('error');
    spectator.detectChanges();

    const action = spectator.query('.stateful-button-action');
    expect(action).toHaveAttribute('role', 'button');
    expect(action).toHaveAttribute('tabindex', '0');
    expect(action).toHaveAttribute('data-what', 'stateful-button-action');
  });

  it('should have fade animation applied to content divs', () => {
    // Animation triggers don't add DOM attributes, so we test for the elements to exist
    // The animation setup is tested through the NoopAnimationsModule in the component setup
    expect(spectator.query('.stateful-button-content--default')).toExist();
    
    spectator.component.state.set('success');
    spectator.detectChanges();
    expect(spectator.query('.stateful-button-content--success')).toExist();
    
    spectator.component.state.set('error');
    spectator.detectChanges();
    expect(spectator.query('.stateful-button-content--error')).toExist();
  });

  it('should properly align content based on state', () => {
    const defaultContent = spectator.query('.stateful-button-content--default');
    expect(defaultContent).toHaveClass('stateful-button-content--default');
    
    spectator.component.state.set('success');
    spectator.detectChanges();
    const successContent = spectator.query('.stateful-button-content--success');
    expect(successContent).toHaveClass('stateful-button-content--success');
    
    spectator.component.state.set('error');
    spectator.detectChanges();
    const errorContent = spectator.query('.stateful-button-content--error');
    expect(errorContent).toHaveClass('stateful-button-content--error');
  });
});