import { Injectable, signal, computed, inject } from '@angular/core';
import { Observable, tap } from 'rxjs';
import { AuthSession, AuthStatus, WebSessionStart } from '../models/auth.model';
import { TelegramSessionApiService } from './telegram-session-api.service';

const WEB_SESSION_COOKIE = 'webSessionID';
const WEB_SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60;

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly api = inject(TelegramSessionApiService);

  private sessionSignal = signal<AuthSession | null>(null);
  private statusSignal = signal<AuthStatus>('unknown');
  private showLoginSignal = signal(false);

  /** Current auth session */
  readonly session = this.sessionSignal.asReadonly();
  /** Current auth status */
  readonly status = this.statusSignal.asReadonly();
  /** Whether user is fully authenticated */
  readonly isAuthenticated = computed(() => this.statusSignal() === 'authenticated');
  /** Whether to show login dialog */
  readonly showLoginDialog = this.showLoginSignal.asReadonly();
  /** Display name of authenticated user */
  readonly displayName = computed(() => this.sessionSignal()?.displayName ?? null);

  private sessionCheckTimer?: ReturnType<typeof setTimeout>;

  constructor() {
    // On init, check existing session via cookie
    this.checkSession();
  }

  /** Check the current webSessionID cookie against the auth backend. */
  checkSession(): void {
    const webSessionID = this.getStoredWebSessionID();

    if (!webSessionID) {
      this.clearAuthState('unauthenticated');
      return;
    }

    this.statusSignal.set('checking');

    this.checkSessionOnce(webSessionID).subscribe(session => {
      if (!session?.active) {
        this.clearAuthState('unauthenticated');
      }
    });
  }

  /** Check session without updating internal state beyond activating on success (used for polling). */
  checkSessionOnce(webSessionID = this.getStoredWebSessionID()): Observable<AuthSession | null> {
    return this.api.checkSessionOnce(webSessionID).pipe(
      tap(session => {
        if (session?.active) {
          this.activateSession(session);
        }
      })
    );
  }

  /**
   * Called after user completes Telegram login.
   */
  onTelegramLoginComplete(): void {
    this.hideLogin();

    if (!this.isAuthenticated()) {
      this.checkSession();
    }
  }

  /** Generate the Telegram login URL for bot-based auth */
  getTelegramLoginUrl(webSessionID: string): string {
    return this.api.getBotLoginUrl(webSessionID);
  }

  /** Generate a Telegram app deep link for mobile login without opening a browser tab. */
  getTelegramAppLoginUrl(webSessionID: string): string {
    return this.api.getBotAppLoginUrl(webSessionID);
  }

  /** Create a backend web session and return the Telegram start link for it. */
  createWebSession(): Observable<WebSessionStart> {
    return this.api.createSession();
  }

  /** Show login dialog (called when user tries to pay without being logged in) */
  requestLogin(): void {
    this.showLoginSignal.set(true);
  }

  /** Hide login dialog */
  hideLogin(): void {
    this.showLoginSignal.set(false);
  }

  /** Logout — clears session on backend and locally */
  logout(): void {
    const webSessionID = this.sessionSignal()?.sessionId || this.getStoredWebSessionID();

    if (!webSessionID) {
      this.clearAuthState('unauthenticated');
      return;
    }

    this.api.logout(webSessionID).subscribe(() => {
      this.clearAuthState('unauthenticated');
    });
  }

  private activateSession(session: AuthSession): void {
    this.sessionSignal.set(session);
    this.statusSignal.set('authenticated');
    this.setStoredWebSessionID(session.sessionId);
    this.scheduleSessionRefresh(session.expires);
  }

  private clearAuthState(status: AuthStatus): void {
    this.sessionSignal.set(null);
    this.statusSignal.set(status);
    this.clearStoredWebSessionID();
    this.clearSessionRefresh();
  }

  /** Schedule a session re-check before it expires */
  private scheduleSessionRefresh(expiresAt: string): void {
    this.clearSessionRefresh();

    const expiresMs = new Date(expiresAt).getTime();
    const nowMs = Date.now();
    // Re-check 60 seconds before expiry, minimum 30s from now
    const refreshIn = Number.isFinite(expiresMs)
      ? Math.max(expiresMs - nowMs - 60_000, 30_000)
      : WEB_SESSION_COOKIE_MAX_AGE_SECONDS * 1000;

    this.sessionCheckTimer = setTimeout(() => {
      this.checkSession();
    }, refreshIn);
  }

  private clearSessionRefresh(): void {
    if (this.sessionCheckTimer) {
      clearTimeout(this.sessionCheckTimer);
      this.sessionCheckTimer = undefined;
    }
  }

  private getStoredWebSessionID(): string | null {
    if (typeof document === 'undefined') {
      return null;
    }

    const cookie = document.cookie
      .split('; ')
      .find(row => row.startsWith(`${WEB_SESSION_COOKIE}=`));

    if (!cookie) {
      return null;
    }

    try {
      return decodeURIComponent(cookie.substring(WEB_SESSION_COOKIE.length + 1));
    } catch {
      return null;
    }
  }

  private setStoredWebSessionID(webSessionID: string): void {
    if (typeof document === 'undefined') {
      return;
    }

    const secure = typeof window !== 'undefined' && window.location.protocol === 'https:' ? '; Secure' : '';
    document.cookie = `${WEB_SESSION_COOKIE}=${encodeURIComponent(webSessionID)}; Max-Age=${WEB_SESSION_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax${secure}`;
  }

  private clearStoredWebSessionID(): void {
    if (typeof document === 'undefined') {
      return;
    }

    document.cookie = `${WEB_SESSION_COOKIE}=; Max-Age=0; Path=/; SameSite=Lax`;
  }
}
