import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, PLATFORM_ID, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

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

const PASSWORD_MIN_LENGTH = 8;
const EMAIL_MAX_LENGTH = 255;
const POST_LOGIN_RETURN_URL_KEY = 'shopit.auth.return-url';

@Component({
  selector: 'app-login-page',
  imports: [ReactiveFormsModule, InputComponent, ButtonComponent],
  templateUrl: './login-page.component.html',
  styleUrl: './login-page.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class LoginPageComponent {
  private readonly formBuilder = inject(FormBuilder);
  private readonly router = inject(Router);
  private readonly route = inject(ActivatedRoute);
  private readonly authService = inject(AuthService);
  private readonly document = inject(DOCUMENT);
  private readonly platformId = inject(PLATFORM_ID);

  private readonly submittedState = signal(false);
  private readonly serverErrorState = signal<string | null>(null);
  private readonly isSubmittingState = signal(false);

  protected readonly form = this.formBuilder.nonNullable.group({
    email: ['', [Validators.required, Validators.email, Validators.maxLength(EMAIL_MAX_LENGTH)]],
    password: ['', [Validators.required, Validators.minLength(PASSWORD_MIN_LENGTH)]]
  });
  protected readonly submitted = this.submittedState.asReadonly();
  protected readonly serverError = this.serverErrorState.asReadonly();
  protected readonly isSubmitting = this.isSubmittingState.asReadonly();

  ngOnInit(): void {
    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    const oauthCode = this.route.snapshot.queryParamMap.get('oauth_code');

    if (oauthCode) {
      this.completeGoogleLogin(oauthCode);
    }
  }

  goToRegister(): void {
    void this.router.navigate(['/register']);
  }

  goToRecoverPassword(): void {
    void this.router.navigate(['/recuperar-contrasena']);
  }

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

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

    this.isSubmittingState.set(true);

    this.authService.login(this.form.getRawValue()).subscribe({
      next: () => {
        this.isSubmittingState.set(false);
        this.redirectToHome();
      },
      error: (error: unknown) => {
        this.isSubmittingState.set(false);
        this.serverErrorState.set(this.resolveErrorMessage(error));
      }
    });
  }

  loginWithGoogle(): void {
    this.serverErrorState.set(null);

    try {
      const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
      if (returnUrl?.startsWith('/') && !returnUrl.startsWith('//')) {
        this.document.defaultView?.sessionStorage.setItem(POST_LOGIN_RETURN_URL_KEY, returnUrl);
      }
      this.authService.loginWithGoogle();
    } catch (error: unknown) {
      this.serverErrorState.set(this.resolveErrorMessage(error));
    }
  }

  protected updateEmail(value: string | number): void {
    this.form.controls.email.setValue(String(value));
  }

  protected updatePassword(value: string | number): void {
    this.form.controls.password.setValue(String(value));
  }

  protected showControlError(controlName: 'email' | 'password'): boolean {
    const control = this.form.controls[controlName];

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

  protected getControlError(controlName: 'email' | 'password'): 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('maxlength')) {
      return `No puede superar los ${EMAIL_MAX_LENGTH} caracteres.`;
    }

    if (control.hasError('email')) {
      return 'Ingresa un email valido.';
    }

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

    return 'El valor ingresado no es valido.';
  }

  protected redirectToHome(): void {
    const requestedUrl =
      this.route.snapshot.queryParamMap.get('returnUrl') ??
      this.document.defaultView?.sessionStorage.getItem(POST_LOGIN_RETURN_URL_KEY);
    const destination =
      requestedUrl?.startsWith('/') && !requestedUrl.startsWith('//')
        ? requestedUrl
        : this.router.serializeUrl(this.router.createUrlTree(['/']));

    this.document.defaultView?.sessionStorage.removeItem(POST_LOGIN_RETURN_URL_KEY);
    this.document.location.assign(destination);
  }

  private completeGoogleLogin(oauthCode: string): void {
    this.isSubmittingState.set(true);
    this.serverErrorState.set(null);

    this.authService.completeGoogleLogin(oauthCode).subscribe({
      next: () => {
        this.isSubmittingState.set(false);
        this.redirectToHome();
      },
      error: (error: unknown) => {
        this.isSubmittingState.set(false);
        this.serverErrorState.set(this.resolveErrorMessage(error));
      }
    });
  }

  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 apiErrors = errorPayload?.errors;
    const emailMessages = apiErrors?.['email'];
    const emailError = Array.isArray(emailMessages) ? emailMessages[0] : null;

    if (typeof emailError === 'string' && emailError.trim()) {
      return emailError;
    }

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

    return 'No se pudo iniciar sesion. Intenta nuevamente.';
  }
}
