import '@angular/compiler';
import { Component, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { TestBed, getTestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

import { MODAL_DATA, ModalRef, ModalService } from '../../../core/services/modal.service';
import { ModalHostComponent } from './modal-host.component';

@Component({
  standalone: true,
  template: `
    <div class="modal-test-content">
      <span class="modal-test-title">{{ data?.title }}</span>
      <button type="button" class="modal-test-close" (click)="close()">Cerrar</button>
    </div>
  `,
})
class ModalContentTestComponent {
  readonly data = inject<{ title: string } | null>(MODAL_DATA);
  readonly modalRef = inject<ModalRef<string>>(ModalRef);

  close(): void {
    this.modalRef.close('accepted');
  }
}

describe('ModalHostComponent', () => {
  let service: ModalService;
  let doc: Document;

  beforeAll(() => {
    try {
      getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
    } catch {
      // Test environment may already be initialized by another setup entrypoint.
    }
  });

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

    service = TestBed.inject(ModalService);
    doc = TestBed.inject(DOCUMENT);
  });

  afterEach(() => {
    doc.body.style.overflow = '';
    doc.body.style.paddingRight = '';
    TestBed.resetTestingModule();
  });

  it('renders nothing when no modal is active', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    fixture.detectChanges();

    expect(fixture.nativeElement.textContent?.trim()).toBe('');
    expect(doc.body.style.overflow).toBe('');
  });

  it('opens and renders the requested component with its title and data', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);

    service.open(ModalContentTestComponent, {
      title: 'Editar producto',
      data: { title: 'Contenido del modal' },
    });
    fixture.detectChanges();

    const title = fixture.nativeElement.querySelector('.modal-shell__title');
    const content = fixture.nativeElement.querySelector('.modal-test-title');

    expect(title?.textContent).toContain('Editar producto');
    expect(content?.textContent).toContain('Contenido del modal');
    expect(doc.body.style.overflow).toBe('hidden');
  });

  it('closes with a result from the child component', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      data: { title: 'Cerrar' },
    });
    const closedSpy = vi.fn();

    ref.afterClosed$.subscribe(closedSpy);
    fixture.detectChanges();

    const closeButton = fixture.nativeElement.querySelector(
      '.modal-test-close',
    ) as HTMLButtonElement;
    closeButton.click();
    fixture.detectChanges();

    expect(closedSpy).toHaveBeenCalledWith('accepted');
    expect(service.activeModal()).toBeNull();
    expect(doc.body.style.overflow).toBe('');
    expect(doc.body.style.paddingRight).toBe('');
  });

  it('closes on backdrop click when enabled', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      data: { title: 'Backdrop' },
      closeOnBackdrop: true,
    });

    fixture.detectChanges();

    const backdrop = fixture.nativeElement.querySelector('.modal-shell') as HTMLDivElement;
    backdrop.click();
    fixture.detectChanges();

    expect(service.activeModal()).toBeNull();
    expect(ref.dismissReason()).toBe('backdrop');
  });

  it('does not close on backdrop click when disabled', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      data: { title: 'Persistente' },
      closeOnBackdrop: false,
    });

    fixture.detectChanges();

    const backdrop = fixture.nativeElement.querySelector('.modal-shell') as HTMLDivElement;
    backdrop.click();
    fixture.detectChanges();

    expect(service.activeModal()?.ref).toBe(ref);
  });

  it('closes on Escape when enabled', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      data: { title: 'Escape' },
      closeOnEscape: true,
    });

    fixture.detectChanges();
    doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
    fixture.detectChanges();

    expect(service.activeModal()).toBeNull();
    expect(ref.dismissReason()).toBe('escape');
  });

  it('keeps the modal open on Escape when disabled', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      data: { title: 'No Escape' },
      closeOnEscape: false,
    });

    fixture.detectChanges();
    doc.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
    fixture.detectChanges();

    expect(service.activeModal()?.ref).toBe(ref);
  });

  it('closes from the shell close button with a programmatic dismiss reason', () => {
    const fixture = TestBed.createComponent(ModalHostComponent);
    const ref = service.open(ModalContentTestComponent, {
      title: 'Con cierre',
      data: { title: 'Boton' },
    });

    fixture.detectChanges();

    const closeButton = fixture.debugElement.query(By.css('.btn-close'))
      .nativeElement as HTMLButtonElement;
    closeButton.click();
    fixture.detectChanges();

    expect(service.activeModal()).toBeNull();
    expect(ref.dismissReason()).toBe('programmatic');
  });
});
