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

import { ConfirmDeleteModalComponent } from '../../shared/components/confirm-delete-modal/confirm-delete-modal.component';
import { ConfirmModalComponent } from '../../shared/components/confirm-modal/confirm-modal.component';
import { SimpleModalComponent } from '../../shared/components/simple-modal/simple-modal.component';
import {
  MODAL_DATA,
  ModalRef,
  ModalService
} from './modal.service';

@Component({
  template: ''
})
class FirstTestModalComponent {}

@Component({
  template: ''
})
class SecondTestModalComponent {}

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

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

  afterEach(() => {
    TestBed.resetTestingModule();
  });

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [ModalService]
    });

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

  it('opens a modal and stores the normalized config', () => {
    const ref = service.open(FirstTestModalComponent, {
      title: 'Confirmar compra',
      data: { productId: 10 },
      size: 'lg',
      closeOnBackdrop: false
    });

    const activeModal = service.activeModal();

    expect(activeModal).not.toBeNull();
    expect(activeModal?.component).toBe(FirstTestModalComponent);
    expect(activeModal?.ref).toBe(ref);
    expect(activeModal?.config).toEqual({
      title: 'Confirmar compra',
      data: { productId: 10 },
      size: 'lg',
      closeOnBackdrop: false,
      closeOnEscape: true,
      showCloseButton: true
    });
  });

  it('emits the close result and clears the active modal', () => {
    const ref = service.open(FirstTestModalComponent);
    const closedSpy = vi.fn();

    ref.afterClosed$.subscribe(closedSpy);
    ref.close({ confirmed: true });

    expect(closedSpy).toHaveBeenCalledWith({ confirmed: true });
    expect(service.activeModal()).toBeNull();
    expect(ref.dismissReason()).toBeNull();
  });

  it('tracks dismiss reasons when closing programmatically', () => {
    const ref = service.open(FirstTestModalComponent);
    const closedSpy = vi.fn();

    ref.afterClosed$.subscribe(closedSpy);
    ref.dismiss();

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

  it('dismisses the current modal as replaced when another one opens', () => {
    const firstRef = service.open(FirstTestModalComponent);
    const firstClosedSpy = vi.fn();

    firstRef.afterClosed$.subscribe(firstClosedSpy);

    const secondRef = service.open(SecondTestModalComponent, {
      title: 'Segundo modal'
    });

    expect(firstClosedSpy).toHaveBeenCalledWith(undefined);
    expect(firstRef.dismissReason()).toBe('replaced');
    expect(service.activeModal()?.ref).toBe(secondRef);
    expect(service.activeModal()?.component).toBe(SecondTestModalComponent);
  });

  it('injects MODAL_DATA and ModalRef into opened components through the host injector contract', () => {
    const ref = service.open(DataTestModalComponent, {
      data: { amount: 3 }
    });

    const activeModal = service.activeModal();

    expect(activeModal?.config.data).toEqual({ amount: 3 });
    expect(activeModal?.ref).toBe(ref);
  });

  it('opens the standard confirm modal with default labels', () => {
    const result$ = service.openConfirm({
      title: 'Confirmar compra',
      content: 'Esto confirmara la compra actual.'
    });

    const activeModal = service.activeModal();

    expect(activeModal?.component).toBe(ConfirmModalComponent);
    expect(result$).toBeDefined();
    expect(activeModal?.config).toEqual({
      title: 'Confirmar compra',
      data: {
        content: 'Esto confirmara la compra actual.',
        confirmLabel: 'Confirmar',
        cancelLabel: 'Cancelar'
      },
      size: 'md',
      closeOnBackdrop: true,
      closeOnEscape: true,
      showCloseButton: true
    });
  });

  it('maps the confirm modal close result to true', async () => {
    const result$ = service.openConfirm({
      title: 'Confirmar compra',
      content: 'Esto confirmara la compra actual.'
    });
    const activeModal = service.activeModal();
    const resultPromise = firstValueFrom(result$);

    activeModal?.ref.close(true);

    await expect(resultPromise).resolves.toBe(true);
  });

  it('maps dismissing a confirm modal to false', async () => {
    const result$ = service.openConfirmDelete({
      title: 'Eliminar producto',
      content: 'Se eliminara el producto.'
    });
    const activeModal = service.activeModal();
    const resultPromise = firstValueFrom(result$);

    activeModal?.ref.dismiss('escape');

    await expect(resultPromise).resolves.toBe(false);
  });

  it('opens the delete confirm modal preserving modal overrides', () => {
    service.openConfirmDelete({
      title: 'Eliminar producto',
      content: 'Se eliminara el producto.',
      confirmLabel: 'Eliminar',
      cancelLabel: 'Conservar',
      size: 'lg',
      closeOnBackdrop: false,
      closeOnEscape: false,
      showCloseButton: false
    });

    const activeModal = service.activeModal();

    expect(activeModal?.component).toBe(ConfirmDeleteModalComponent);
    expect(activeModal?.config).toEqual({
      title: 'Eliminar producto',
      data: {
        content: 'Se eliminara el producto.',
        confirmLabel: 'Eliminar',
        cancelLabel: 'Conservar'
      },
      size: 'lg',
      closeOnBackdrop: false,
      closeOnEscape: false,
      showCloseButton: false
    });
  });

  it('opens the simple modal with default button label', () => {
    service.openSimple({
      title: 'Aviso',
      content: 'Este es un aviso simple.'
    });

    const activeModal = service.activeModal();

    expect(activeModal?.component).toBe(SimpleModalComponent);
    expect(activeModal?.config).toEqual({
      title: 'Aviso',
      data: {
        content: 'Este es un aviso simple.',
        buttonLabel: 'Entendido'
      },
      size: 'md',
      closeOnBackdrop: true,
      closeOnEscape: true,
      showCloseButton: true
    });
  });

  it('maps the simple modal close result to undefined', async () => {
    const result$ = service.openSimple({
      title: 'Aviso',
      content: 'Este es un aviso simple.'
    });
    const activeModal = service.activeModal();
    const resultPromise = firstValueFrom(result$);

    activeModal?.ref.close();

    await expect(resultPromise).resolves.toBeUndefined();
  });
});

@Component({
  template: ''
})
class DataTestModalComponent {
  readonly data = inject(MODAL_DATA);
  readonly modalRef = inject(ModalRef);
}
