import { HttpResponse } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { ActivatedRoute, convertToParamMap, provideRouter, Router } from '@angular/router';
import { of, throwError } from 'rxjs';

import { AuthService } from '../../../../core/services/auth/auth.service';
import { ModalService } from '../../../../core/services/modal.service';
import { ToastService } from '../../../../core/services/toast.service';
import { ResetPasswordPageComponent } from './reset-password-page.component';

describe('ResetPasswordPageComponent', () => {
  let resetPassword: ReturnType<typeof vi.fn>;
  let showDangerToast: ReturnType<typeof vi.fn>;

  beforeEach(() => {
    TestBed.resetTestingModule();
    vi.spyOn(console, 'error').mockImplementation(() => undefined);
    vi.spyOn(console, 'warn').mockImplementation(() => undefined);
    resetPassword = vi.fn(() => of(new HttpResponse({
      body: {
        message: 'Contraseña modificada correctamente.',
        status: 'used' as const,
      },
      status: 200,
    })));
    showDangerToast = vi.fn();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  function resetProviders(modalService: { openSimple: ReturnType<typeof vi.fn> }) {
    return [
      provideRouter([]),
      { provide: AuthService, useValue: { resetPassword } },
      { provide: ModalService, useValue: modalService },
      { provide: ToastService, useValue: { danger: showDangerToast } },
      {
        provide: ActivatedRoute,
        useValue: {
          snapshot: {
            queryParamMap: convertToParamMap({
              email: 'ada@example.com',
              code: '1234',
            }),
          },
        },
      },
    ];
  }

  it('shows and hides both password fields with either visibility control', async () => {
    const modalService = {
      openSimple: vi.fn(),
    };

    await TestBed.configureTestingModule({
      imports: [ResetPasswordPageComponent],
      providers: resetProviders(modalService),
    }).compileComponents();

    const fixture = TestBed.createComponent(ResetPasswordPageComponent);
    fixture.detectChanges();

    const getPasswordInputs = () =>
      Array.from(fixture.nativeElement.querySelectorAll('input')) as HTMLInputElement[];
    const getVisibilityButtons = () =>
      Array.from(
        fixture.nativeElement.querySelectorAll('button[aria-label]'),
      ) as HTMLButtonElement[];

    expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']);

    getVisibilityButtons()[0].click();
    fixture.detectChanges();

    expect(getPasswordInputs().map((input) => input.type)).toEqual(['text', 'text']);
    expect(getVisibilityButtons().map((button) => button.getAttribute('aria-label'))).toEqual([
      'Ocultar contraseña',
      'Ocultar contraseña',
    ]);

    getVisibilityButtons()[1].click();
    fixture.detectChanges();

    expect(getPasswordInputs().map((input) => input.type)).toEqual(['password', 'password']);
  });

  it('rejects passwords that do not match', async () => {
    const modalService = {
      openSimple: vi.fn(),
    };

    await TestBed.configureTestingModule({
      imports: [ResetPasswordPageComponent],
      providers: resetProviders(modalService),
    }).compileComponents();

    const fixture = TestBed.createComponent(ResetPasswordPageComponent);
    const component = fixture.componentInstance as any;

    component.form.setValue({
      password: 'Secret!123',
      password_confirmation: 'Different!123',
    });
    component.onSubmit();

    expect(component.form.hasError('passwordMismatch')).toBe(true);
    expect(component.getControlError('password_confirmation')).toBe(
      'Las contraseñas no coinciden.',
    );
    expect(modalService.openSimple).not.toHaveBeenCalled();
    expect(resetPassword).not.toHaveBeenCalled();
  });

  it('shows the success modal and returns to login for matching passwords', async () => {
    const modalService = {
      openSimple: vi.fn().mockReturnValue(of(undefined)),
    };

    await TestBed.configureTestingModule({
      imports: [ResetPasswordPageComponent],
      providers: resetProviders(modalService),
    }).compileComponents();

    const fixture = TestBed.createComponent(ResetPasswordPageComponent);
    const component = fixture.componentInstance as any;
    const router = TestBed.inject(Router);
    const navigateSpy = vi.spyOn(router, 'navigate').mockResolvedValue(true);

    component.form.setValue({
      password: 'Secret!123',
      password_confirmation: 'Secret!123',
    });
    component.onSubmit();

    expect(component.form.valid).toBe(true);
    expect(component.getControlError('password_confirmation')).toBeNull();
    expect(resetPassword).toHaveBeenCalledWith({
      email: 'ada@example.com',
      codigo: '1234',
      password: 'Secret!123',
      password_confirmation: 'Secret!123',
    });
    expect(modalService.openSimple).toHaveBeenCalledWith({
      content: 'Contraseña modificada correctamente',
      buttonLabel: 'Cerrar',
    });
    expect(navigateSpy).toHaveBeenCalledWith(['/login']);
  });

  it('shows the API error without opening the success modal', async () => {
    resetPassword.mockReturnValue(throwError(() => ({
      error: {
        errors: {
          codigo: ['La solicitud de recuperación es inválida o ya fue utilizada.'],
        },
      },
    })));
    const modalService = {
      openSimple: vi.fn(),
    };

    await TestBed.configureTestingModule({
      imports: [ResetPasswordPageComponent],
      providers: resetProviders(modalService),
    }).compileComponents();

    const fixture = TestBed.createComponent(ResetPasswordPageComponent);
    const component = fixture.componentInstance as any;

    component.form.setValue({
      password: 'Secret!123',
      password_confirmation: 'Secret!123',
    });
    component.onSubmit();

    expect(modalService.openSimple).not.toHaveBeenCalled();
    expect(component.serverError()).toBe(
      'La solicitud de recuperación es inválida o ya fue utilizada.',
    );
    expect(showDangerToast).toHaveBeenCalled();
  });
});
