import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
import {
  AbstractControl,
  FormBuilder,
  ReactiveFormsModule,
  ValidationErrors,
  ValidatorFn,
  Validators,
} from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { AuthService } from '../../../../core/services/auth/auth.service';
import { ModalService } from '../../../../core/services/modal.service';
import { ToastService } from '../../../../core/services/toast.service';
import { ButtonComponent } from '../../../../shared/components/button/button.component';
import { InputComponent } from '../../../../shared/components/input/input.component';

const PASSWORD_MIN_LENGTH = 8;

const passwordsMatchValidator: ValidatorFn = (
  control: AbstractControl,
): ValidationErrors | null => {
  const password = control.get('password')?.value;
  const passwordConfirmation = control.get('password_confirmation')?.value;

  if (!password || !passwordConfirmation) {
    return null;
  }

  return password === passwordConfirmation ? null : { passwordMismatch: true };
};

type PasswordControlName = 'password' | 'password_confirmation';

@Component({
  selector: 'app-reset-password-page',
  imports: [ReactiveFormsModule, InputComponent, ButtonComponent],
  templateUrl: './reset-password-page.component.html',
  styleUrl: './reset-password-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ResetPasswordPageComponent {
  private readonly formBuilder = inject(FormBuilder);
  private readonly router = inject(Router);
  private readonly route = inject(ActivatedRoute);
  private readonly authService = inject(AuthService);
  private readonly modalService = inject(ModalService);
  private readonly toastService = inject(ToastService);
  private readonly submittedState = signal(false);
  private readonly isSubmittingState = signal(false);
  private readonly serverErrorState = signal<string | null>(null);
  private readonly passwordVisibleState = signal(false);

  private readonly email = this.route.snapshot.queryParamMap.get('email') ?? '';
  private readonly code = this.route.snapshot.queryParamMap.get('code') ?? '';

  protected readonly form = this.formBuilder.nonNullable.group(
    {
      password: [
        '',
        [
          Validators.required,
          Validators.minLength(PASSWORD_MIN_LENGTH),
          Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).+$/),
        ],
      ],
      password_confirmation: ['', [Validators.required]],
    },
    {
      validators: [passwordsMatchValidator],
    },
  );
  protected readonly submitted = this.submittedState.asReadonly();
  protected readonly isSubmitting = this.isSubmittingState.asReadonly();
  protected readonly serverError = this.serverErrorState.asReadonly();
  protected readonly passwordVisible = this.passwordVisibleState.asReadonly();

  protected setPasswordVisibility(visible: boolean): void {
    this.passwordVisibleState.set(visible);
  }

  protected updatePassword(controlName: PasswordControlName, value: string | number): void {
    this.form.controls[controlName].setValue(String(value));
  }

  protected showControlError(controlName: PasswordControlName): boolean {
    const control = this.form.controls[controlName];
    const hasPasswordMismatch =
      controlName === 'password_confirmation' &&
      this.form.hasError('passwordMismatch') &&
      (control.touched || this.submitted());

    return (control.invalid && (control.touched || this.submitted())) || hasPasswordMismatch;
  }

  protected getControlError(controlName: PasswordControlName): string | null {
    const control = this.form.controls[controlName];

    if (!this.showControlError(controlName)) {
      return null;
    }

    if (control.hasError('required')) {
      return 'Este campo es obligatorio.';
    }

    if (control.hasError('minlength')) {
      return `Debe tener al menos ${PASSWORD_MIN_LENGTH} caracteres.`;
    }

    if (controlName === 'password' && control.hasError('pattern')) {
      return 'Debe contener mayúscula, minúscula y un carácter especial.';
    }

    if (controlName === 'password_confirmation' && this.form.hasError('passwordMismatch')) {
      return 'Las contraseñas no coinciden.';
    }

    return 'El valor ingresado no es válido.';
  }

  protected onSubmit(): void {
    this.submittedState.set(true);
    this.serverErrorState.set(null);

    if (this.form.invalid || this.isSubmitting()) {
      this.form.markAllAsTouched();
      return;
    }

    if (!this.email || !/^\d{4}$/.test(this.code)) {
      console.warn('Password reset cannot start without a validated recovery request.');
      this.showServerError('La solicitud de recuperación es inválida.');
      return;
    }

    this.isSubmittingState.set(true);

    this.authService.resetPassword({
      email: this.email,
      codigo: this.code,
      ...this.form.getRawValue(),
    }).subscribe({
      next: (response) => {
        this.isSubmittingState.set(false);

        if (response.status !== 200 || response.body?.status !== 'used') {
          console.warn('Password reset returned an unexpected status.', {
            httpStatus: response.status,
            attemptStatus: response.body?.status ?? null,
          });
          this.showServerError('No se pudo modificar la contraseña. Intenta nuevamente.');
          return;
        }

        this.modalService
          .openSimple({
            content: 'Contraseña modificada correctamente',
            buttonLabel: 'Cerrar',
          })
          .subscribe(() => {
            void this.router.navigate(['/login']);
          });
      },
      error: (error: unknown) => {
        this.isSubmittingState.set(false);
        console.error('Password reset request failed.', error);
        this.showServerError(this.resolveErrorMessage(error));
      },
    });
  }

  private showServerError(message: string): void {
    this.serverErrorState.set(message);
    this.toastService.danger(message);
  }

  private resolveErrorMessage(error: unknown): string {
    const errorPayload =
      typeof error === 'object' && error !== null && 'error' in error
        ? (error as { error?: { errors?: Record<string, string[]>; message?: string } }).error
        : undefined;
    const errors = errorPayload?.errors;

    for (const field of ['codigo', 'password'] as const) {
      const messages = errors?.[field];

      if (Array.isArray(messages) && typeof messages[0] === 'string' && messages[0].trim()) {
        return messages[0];
      }
    }

    if (typeof errorPayload?.message === 'string' && errorPayload.message.trim()) {
      return errorPayload.message;
    }

    return 'No se pudo modificar la contraseña. Intenta nuevamente.';
  }
}
