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

/**
 * Admin login uses the exact same Telegram QR/session API as the customer
 * login (TelegramSessionApiService, `{authApiUrl}/users/sessions`) - there is
 * no separate admin backend endpoint, and none should be invented client-side.
 * Only the *storage* is kept separate from AuthService, so an admin QR scan
 * never authenticates the customer session or vice versa: distinct cookie
 * name, distinct signals, distinct guard/interceptor.
 *
 * Backend gap this creates (see docs/backend/BACKEND-INTEGRATION.md §2.5): since the session
 * API itself has no concept of "admin", the frontend cannot tell an admin
 * Telegram session from a regular one. Actual admin authorization must be
 * enforced server-side when admin API calls are made with the resulting
 * session id - the frontend only decides where to *store* the result.
 */
const ADMIN_SESSION_COOKIE = 'adminSessionID';
const ADMIN_TOKEN_STORAGE_KEY = 'adminToken';
const ADMIN_REFRESH_STORAGE_KEY = 'adminRefreshToken';
const ADMIN_SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60;

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

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

  readonly session = this.sessionSignal.asReadonly();
  readonly status = this.statusSignal.asReadonly();
  readonly isAuthenticated = computed(() => this.statusSignal() === 'authenticated');
  readonly showLoginDialog = this.showLoginSignal.asReadonly();
  readonly displayName = computed(() => this.sessionSignal()?.displayName ?? null);

  private sessionCheckTimer?: ReturnType<typeof setTimeout>;

  constructor() {
    this.checkSession();
  }

  checkSession(): void {
    const webSessionID = this.getStoredAdminSessionID();
    if (!webSessionID) {
      this.clearAuthState('unauthenticated');
      return;
    }

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

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

  /** Create a backend web session - identical call to the customer login (TelegramSessionApiService.createSession). */
  createWebSession(): Observable<WebSessionStart> {
    return this.api.createSession();
  }

  getAdminAppLoginUrl(webSessionID: string): string {
    return this.api.getBotAppLoginUrl(webSessionID);
  }

  onLoginComplete(): void {
    this.hideLogin();
    if (!this.isAuthenticated()) {
      this.checkSession();
    }
  }

  requestLogin(): void {
    this.showLoginSignal.set(true);
  }

  /**
   * Dev-only shortcut for local testing without a reachable Telegram/session
   * backend: fabricates a local session and activates it directly, skipping
   * the QR flow entirely. No-ops in production builds (checked at runtime,
   * not just build-time, so it is safe even if this code ships). Never call
   * this from anywhere reachable in a production build.
   */
  devBypassLogin(): void {
    if (environment.production) {
      return;
    }
    this.hideLogin();
    this.activateSession({
      sessionId: `dev-bypass-${Date.now()}`,
      userId: 0,
      username: 'dev-admin',
      displayName: 'Dev Admin (local bypass)',
      active: true,
      expires: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
    });
  }

  hideLogin(): void {
    this.showLoginSignal.set(false);
  }

  logout(): void {
    const webSessionID = this.sessionSignal()?.sessionId || this.getStoredAdminSessionID();
    if (!webSessionID) {
      this.clearAuthState('unauthenticated');
      return;
    }

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

  /** JWT pair storage, reserved for once the backend issues admin access/refresh tokens. Unused until then. */
  getAdminToken(): string | null {
    return typeof localStorage === 'undefined' ? null : localStorage.getItem(ADMIN_TOKEN_STORAGE_KEY);
  }

  setAdminTokens(token: string, refreshToken: string): void {
    if (typeof localStorage === 'undefined') {
      return;
    }
    localStorage.setItem(ADMIN_TOKEN_STORAGE_KEY, token);
    localStorage.setItem(ADMIN_REFRESH_STORAGE_KEY, refreshToken);
  }

  clearAdminTokens(): void {
    if (typeof localStorage === 'undefined') {
      return;
    }
    localStorage.removeItem(ADMIN_TOKEN_STORAGE_KEY);
    localStorage.removeItem(ADMIN_REFRESH_STORAGE_KEY);
  }

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

  private clearAuthState(status: AdminAuthStatus): void {
    this.sessionSignal.set(null);
    this.statusSignal.set(status);
    this.clearStoredAdminSessionID();
    this.clearAdminTokens();
    this.clearSessionRefresh();
  }

  private scheduleSessionRefresh(expiresAt: string): void {
    this.clearSessionRefresh();
    const expiresMs = new Date(expiresAt).getTime();
    const nowMs = Date.now();
    const refreshIn = Number.isFinite(expiresMs)
      ? Math.max(expiresMs - nowMs - 60_000, 30_000)
      : ADMIN_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 getStoredAdminSessionID(): string | null {
    if (typeof document === 'undefined') {
      return null;
    }
    const cookie = document.cookie.split('; ').find(row => row.startsWith(`${ADMIN_SESSION_COOKIE}=`));
    if (!cookie) {
      return null;
    }
    try {
      return decodeURIComponent(cookie.substring(ADMIN_SESSION_COOKIE.length + 1));
    } catch {
      return null;
    }
  }

  private setStoredAdminSessionID(webSessionID: string): void {
    if (typeof document === 'undefined') {
      return;
    }
    const secure = typeof window !== 'undefined' && window.location.protocol === 'https:' ? '; Secure' : '';
    document.cookie = `${ADMIN_SESSION_COOKIE}=${encodeURIComponent(webSessionID)}; Max-Age=${ADMIN_SESSION_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Strict${secure}`;
  }

  private clearStoredAdminSessionID(): void {
    if (typeof document === 'undefined') {
      return;
    }
    document.cookie = `${ADMIN_SESSION_COOKIE}=; Max-Age=0; Path=/; SameSite=Strict`;
  }
}
