import { effect, inject, Injectable, DestroyRef, signal, untracked } from '@angular/core';
import { MissionStateResponse } from '@fog-explorer/api-interfaces';
import { EbsApiService } from '../ebs/ebs-api.service';
import { TwitchAuthService } from '../twitch/twitch-auth.service';

const POLL_INTERVAL_MS = 20_000;

@Injectable({ providedIn: 'root' })
export class MissionStateStore {
  private readonly ebs = inject(EbsApiService);
  private readonly authService = inject(TwitchAuthService);
  private readonly destroyRef = inject(DestroyRef);

  private readonly _state = signal<MissionStateResponse>(null);
  private readonly _loading = signal(false);
  private readonly _starting = signal(false);
  private readonly _error = signal<unknown>(null);
  private readonly _initializing = signal(true);

  readonly state = this._state.asReadonly();
  readonly loading = this._loading.asReadonly();
  readonly starting = this._starting.asReadonly();
  readonly error = this._error.asReadonly();
  readonly initializing = this._initializing.asReadonly();

  private pollTimer: ReturnType<typeof setInterval> | null = null;

  constructor() {
    // Poll while the mission is active and the overlay is visible.
    // PubSub is the primary update path; polling reconciles missed messages.
    effect(() => {
      const shouldPoll =
        this._state()?.mission.status === 'active' &&
        this.authService.isVisible();
      untracked(() => (shouldPoll ? this.startPolling() : this.stopPolling()));
    });

    this.destroyRef.onDestroy(() => this.stopPolling());
  }

  init(): void {
    this.fetchState();
    this.subscribePubSub();
  }

  /** Call when the overlay becomes visible after being hidden to reconcile missed ticks. */
  refresh(): void {
    this.fetchState();
  }

  abandonMission(): void {
    if (this._starting()) return;
    this._error.set(null);
    this.ebs.abandonMission().subscribe({
      next: () => this._state.set(null),
      error: (err) => this._error.set(err),
    });
  }

  startMission(difficulty: 1 | 2 | 3, durationMinutes: 10 | 20 | 30, characterName?: string): void {
    if (this._starting()) return;
    this._starting.set(true);
    this._error.set(null);
    this.ebs.startMission({ difficulty, durationMinutes, characterName }).subscribe({
      next: (state) => {
        this._state.set(state);
        this._starting.set(false);
      },
      error: (err) => {
        this._starting.set(false);
        this._error.set(err);
      },
    });
  }

  private fetchState(): void {
    if (!this.authService.auth()) return;

    this._loading.set(true);
    this.ebs.getMissionState().subscribe({
      next: (state) => {
        this._state.set(state);
        this._loading.set(false);
        this._initializing.set(false);
        this._error.set(null);
      },
      error: (err) => {
        this._loading.set(false);
        this._initializing.set(false);
        this._error.set(err);
      },
    });
  }

  private startPolling(): void {
    if (this.pollTimer !== null) return;
    this.pollTimer = setInterval(() => this.fetchState(), POLL_INTERVAL_MS);
  }

  private stopPolling(): void {
    if (this.pollTimer === null) return;
    clearInterval(this.pollTimer);
    this.pollTimer = null;
  }

  private subscribePubSub(): void {
    if (!window.Twitch?.ext) return;

    const listener = (_target: string, _contentType: string, _message: string): void => {
      if (!this.authService.isVisible()) return;
      this.fetchState();
    };

    window.Twitch.ext.listen('broadcast', listener);
    this.destroyRef.onDestroy(() => window.Twitch?.ext.unlisten('broadcast', listener));
  }
}
