import { Injectable, Signal, computed, effect, inject, signal } from '@angular/core';
import { AdminOrder } from '../../orders/models/admin-order.model';
import { ADMIN_ORDERS_GATEWAY } from '../../orders/services/admin-orders-gateway.token';
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
import { UserNotificationService } from '../../../website/user-experience/services/user-notification.service';
import { LanguageService } from '../../../../services/language.service';
import { TranslateService } from '../../../../i18n/translate.service';
import { AdminAuthService } from '@marketplaces/auth';

const LAST_NOTIFIED_KEY = 'adminOrderWatcher.lastNotifiedOrderId.v1';
const LAST_NOTIFIED_AT_KEY = 'adminOrderWatcher.lastNotifiedOrderCreatedAt.v1';
const LAST_ACKNOWLEDGED_KEY = 'adminOrderWatcher.lastAcknowledgedOrderId.v1';
const LAST_ACKNOWLEDGED_AT_KEY = 'adminOrderWatcher.lastAcknowledgedOrderCreatedAt.v1';
const POLL_INTERVAL_KEY = 'adminOrderWatcher.pollIntervalMs.v1';
export const DEFAULT_POLL_INTERVAL_MS = 15000;
const MIN_POLL_INTERVAL_MS = 1000;
const RECENT_ORDERS_LIMIT = 20;
const TOAST_DURATION_MS = 4000;

@Injectable({ providedIn: 'root' })
export class AdminOrderWatcherService {
  private readonly gateway = inject(ADMIN_ORDERS_GATEWAY);
  private readonly storage = inject(LocalStorageService);
  private readonly notifications = inject(UserNotificationService);
  private readonly languageService = inject(LanguageService);
  private readonly i18n = inject(TranslateService);
  private readonly adminAuth = inject(AdminAuthService);

  private readonly recentOrdersSignal = signal<AdminOrder[]>([]);
  readonly recentOrders: Signal<AdminOrder[]> = this.recentOrdersSignal.asReadonly();

  private readonly lastAcknowledgedOrderIdSignal = signal<string | null>(this.storage.getItem(LAST_ACKNOWLEDGED_KEY));
  private readonly lastAcknowledgedOrderCreatedAtSignal = signal<string | null>(this.storage.getItem(LAST_ACKNOWLEDGED_AT_KEY));

  readonly unreadCount = computed(() => {
    const orders = this.recentOrdersSignal();
    if (orders.length === 0) {
      return 0;
    }
    const ackId = this.lastAcknowledgedOrderIdSignal();
    if (ackId === null) {
      return orders.length;
    }
    const idx = orders.findIndex(order => order.id === ackId);
    if (idx !== -1) {
      return idx;
    }
    // The acknowledged order id is no longer present in the current page
    // (deleted, or many orders arrived since ack) - fall back to a timestamp
    // comparison instead of treating the whole page as unread.
    const ackCreatedAt = this.lastAcknowledgedOrderCreatedAtSignal();
    return ackCreatedAt === null ? orders.length : orders.filter(order => order.createdAt > ackCreatedAt).length;
  });

  private readonly intervalMsSignal = signal<number>(this.readStoredIntervalMs());
  readonly intervalMs: Signal<number> = this.intervalMsSignal.asReadonly();

  private lastNotifiedOrderId: string | null = this.storage.getItem(LAST_NOTIFIED_KEY);
  private lastNotifiedOrderCreatedAt: string | null = this.storage.getItem(LAST_NOTIFIED_AT_KEY);
  private timerId: ReturnType<typeof setInterval> | null = null;
  private started = false;

  constructor() {
    // Reacts to the auth signal itself rather than component lifecycle: nothing
    // reliably destroys AdminLayoutComponent on logout (no navigation happens),
    // so polling must stop/start off isAuthenticated directly to avoid leaking
    // admin order toasts onto the public storefront after logout.
    effect(() => {
      if (this.adminAuth.isAuthenticated()) {
        this.start();
      } else {
        this.stop();
      }
    });
  }

  start(): void {
    if (this.started) {
      return;
    }
    this.started = true;
    this.poll();
    this.scheduleNext();
  }

  stop(): void {
    if (this.timerId !== null) {
      clearInterval(this.timerId);
      this.timerId = null;
    }
    this.started = false;
  }

  setIntervalSeconds(seconds: number): boolean {
    if (!Number.isFinite(seconds) || seconds < MIN_POLL_INTERVAL_MS / 1000) {
      return false;
    }
    const ms = Math.round(seconds * 1000);
    this.intervalMsSignal.set(ms);
    this.storage.setItem(POLL_INTERVAL_KEY, String(ms));
    if (this.started) {
      this.scheduleNext();
    }
    return true;
  }

  markAllSeen(): void {
    const newest = this.recentOrdersSignal()[0];
    if (!newest) {
      return;
    }
    this.lastAcknowledgedOrderIdSignal.set(newest.id);
    this.lastAcknowledgedOrderCreatedAtSignal.set(newest.createdAt);
    this.storage.setItem(LAST_ACKNOWLEDGED_KEY, newest.id);
    this.storage.setItem(LAST_ACKNOWLEDGED_AT_KEY, newest.createdAt);
  }

  private scheduleNext(): void {
    if (this.timerId !== null) {
      clearInterval(this.timerId);
    }
    this.timerId = setInterval(() => this.poll(), this.intervalMsSignal());
  }

  private poll(): void {
    this.gateway.loadOrders({ search: '', status: 'all', page: 1, pageSize: RECENT_ORDERS_LIMIT }).subscribe({
      next: result => this.handleOrders(result.items),
      error: err => console.error('Error polling for new orders:', err),
    });
  }

  private handleOrders(items: AdminOrder[]): void {
    this.recentOrdersSignal.set(items);

    if (items.length === 0) {
      return;
    }

    const isFirstPoll = this.lastNotifiedOrderId === null;
    const notifyIndex = isFirstPoll ? -1 : items.findIndex(order => order.id === this.lastNotifiedOrderId);
    // When the last-notified order id is no longer present in the current page
    // (deleted, or more than RECENT_ORDERS_LIMIT orders arrived since the last
    // poll), fall back to a timestamp comparison instead of treating the whole
    // page as new - otherwise a single missing id could fire a burst of up to
    // RECENT_ORDERS_LIMIT toasts.
    const newOrders = isFirstPoll
      ? []
      : notifyIndex !== -1
        ? items.slice(0, notifyIndex)
        : items.filter(order => this.lastNotifiedOrderCreatedAt !== null && order.createdAt > this.lastNotifiedOrderCreatedAt);

    this.lastNotifiedOrderId = items[0].id;
    this.lastNotifiedOrderCreatedAt = items[0].createdAt;
    this.storage.setItem(LAST_NOTIFIED_KEY, this.lastNotifiedOrderId);
    this.storage.setItem(LAST_NOTIFIED_AT_KEY, this.lastNotifiedOrderCreatedAt);

    if (isFirstPoll) {
      // Nothing existed to compare against yet - treat current orders as already
      // acknowledged so a fresh admin session doesn't see the whole history as unread.
      if (this.lastAcknowledgedOrderIdSignal() === null) {
        this.lastAcknowledgedOrderIdSignal.set(items[0].id);
        this.lastAcknowledgedOrderCreatedAtSignal.set(items[0].createdAt);
        this.storage.setItem(LAST_ACKNOWLEDGED_KEY, items[0].id);
        this.storage.setItem(LAST_ACKNOWLEDGED_AT_KEY, items[0].createdAt);
      }
      return;
    }

    for (let i = newOrders.length - 1; i >= 0; i--) {
      const order = newOrders[i];
      this.notifications.show(
        this.i18n.t('adminShell.topbar.notificationNewOrder', { orderNumber: order.orderNumber }),
        'info',
        TOAST_DURATION_MS,
        [this.languageService.currentLanguage(), 'backoffice', 'orders', order.id]
      );
    }
  }

  private readStoredIntervalMs(): number {
    const stored = Number(this.storage.getItem(POLL_INTERVAL_KEY));
    return Number.isFinite(stored) && stored >= MIN_POLL_INTERVAL_MS ? stored : DEFAULT_POLL_INTERVAL_MS;
  }
}
