import { DOCUMENT } from '@angular/common';
import { DestroyRef, effect, inject, Injectable, OnDestroy, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Router } from '@angular/router';
import { merge, fromEvent, Subscription } from 'rxjs';
import { throttleTime } from 'rxjs/operators';
import { environment } from '../../../../environments/environment';
import { AuthService } from '../auth/auth.service';

@Injectable()
export class SessionTimeoutService implements OnDestroy {
  private readonly document = inject(DOCUMENT);
  private readonly router = inject(Router);
  private readonly authService = inject(AuthService);
  private readonly destroyRef = inject(DestroyRef);

  readonly showWarning = signal(false);
  readonly remainingSeconds = signal(0);

  private readonly warningAfterMs = environment.sessionTimeout?.warningAfterMs ?? 25 * 60 * 1000;
  private readonly logoutAfterMs = environment.sessionTimeout?.logoutAfterMs ?? 30 * 60 * 1000;

  private activitySubscription: Subscription | null = null;
  private warningTimer: ReturnType<typeof setTimeout> | null = null;
  private logoutTimer: ReturnType<typeof setTimeout> | null = null;
  private countdownTimer: ReturnType<typeof setInterval> | null = null;
  private logoutDeadline = 0;
  private trackingEnabled = false;

  constructor() {
    effect(() => {
      if (this.authService.currentUserSignal()) {
        this.start();
        return;
      }

      this.stop();
    });
  }

  start(): void {
    if (this.trackingEnabled) {
      this.resetTimers();
      return;
    }

    this.trackingEnabled = true;
    this.bindActivityTracking();
    this.resetTimers();
  }

  stop(): void {
    this.trackingEnabled = false;
    this.showWarning.set(false);
    this.remainingSeconds.set(0);
    this.clearTimers();
    this.activitySubscription?.unsubscribe();
    this.activitySubscription = null;
  }

  staySignedIn(): void {
    if (!this.trackingEnabled) {
      return;
    }

    this.resetTimers();
  }

  logoutNow(): void {
    this.handleTimeoutLogout();
  }

  ngOnDestroy(): void {
    this.stop();
  }

  private bindActivityTracking(): void {
    if (this.activitySubscription) {
      return;
    }

    this.activitySubscription = merge(
      fromEvent(this.document, 'mousemove'),
      fromEvent(this.document, 'keydown'),
      fromEvent(this.document, 'click'),
      fromEvent(window, 'scroll')
    )
      .pipe(throttleTime(1000), takeUntilDestroyed(this.destroyRef))
      .subscribe(() => {
        if (!this.trackingEnabled || !this.authService.currentUserSignal()) {
          return;
        }

        this.resetTimers();
      });
  }

  private resetTimers(): void {
    if (!this.trackingEnabled) {
      return;
    }

    this.clearTimers();
    this.showWarning.set(false);
    this.remainingSeconds.set(0);

    const safeWarningDelay = Math.max(Math.min(this.warningAfterMs, this.logoutAfterMs), 0);
    const safeLogoutDelay = Math.max(this.logoutAfterMs, 0);

    this.warningTimer = setTimeout(() => {
      this.showWarning.set(true);
      this.logoutDeadline = Date.now() + Math.max(safeLogoutDelay - safeWarningDelay, 0);
      this.updateRemainingSeconds();
      this.countdownTimer = setInterval(() => {
        this.updateRemainingSeconds();
      }, 1000);
    }, safeWarningDelay);

    this.logoutTimer = setTimeout(() => {
      this.handleTimeoutLogout();
    }, safeLogoutDelay);
  }

  private updateRemainingSeconds(): void {
    const remainingMs = Math.max(this.logoutDeadline - Date.now(), 0);
    this.remainingSeconds.set(Math.ceil(remainingMs / 1000));
  }

  private handleTimeoutLogout(): void {
    const returnUrl = this.router.url.startsWith('/auth') ? '/' : this.router.url;
    this.stop();
    this.authService.logout();
    void this.router.navigate(['/auth/login'], {
      queryParams: returnUrl && returnUrl !== '/' ? { returnUrl } : undefined,
    });
  }

  private clearTimers(): void {
    if (this.warningTimer) {
      clearTimeout(this.warningTimer);
      this.warningTimer = null;
    }

    if (this.logoutTimer) {
      clearTimeout(this.logoutTimer);
      this.logoutTimer = null;
    }

    if (this.countdownTimer) {
      clearInterval(this.countdownTimer);
      this.countdownTimer = null;
    }
  }
}
