import {
  Injectable,
  OnDestroy,
  NgZone,
  inject
} from '@angular/core';
import { Store } from '@ngrx/store';
import { UsersActions } from '../../store/users/users.actions';
import { selectManualStatus, selectCurrentUser } from '../../store/users/users.selectors';
import { RealtimeSessionFacade } from '../realtime';
import { NotificationAudioService } from './notification-audio.service';
import { UserStatus } from '../../shared-kernel';

const BROWSER_IDLE_POLL_MS = 10_000;
const BROWSER_IDLE_THRESHOLD_MS = 15 * 60 * 1000; // 15 minutes

interface ElectronIdleApi {
  onIdleStateChanged: (listener: (state: 'active' | 'idle') => void) => () => void;
  getIdleState: () => Promise<'active' | 'idle'>;
}

type IdleAwareWindow = Window & {
  electronAPI?: ElectronIdleApi;
};

/**
 * Orchestrates user status based on idle detection (Electron powerMonitor
 * or browser-fallback) and manual overrides (e.g. Do Not Disturb).
 *
 * Manual status always takes priority over automatic idle detection.
 * When manual status is cleared, the service falls back to automatic.
 */
@Injectable({ providedIn: 'root' })
export class UserStatusService implements OnDestroy {
  private store = inject(Store);
  private zone = inject(NgZone);
  private webrtc = inject(RealtimeSessionFacade);
  private audio = inject(NotificationAudioService);
  private readonly manualStatus = this.store.selectSignal(selectManualStatus);
  private readonly currentUser = this.store.selectSignal(selectCurrentUser);

  private electronCleanup: (() => void) | null = null;
  private browserPollTimer: ReturnType<typeof setInterval> | null = null;
  private lastActivityTimestamp = Date.now();
  private browserActivityListeners: (() => void)[] = [];
  private currentAutoStatus: UserStatus = 'online';
  private started = false;

  start(): void {
    if (this.started)
      return;

    this.started = true;

    if (this.getElectronIdleApi()?.onIdleStateChanged) {
      this.startElectronIdleDetection();
    } else {
      this.startBrowserIdleDetection();
    }
  }

  /** Set a manual status override (e.g. DND = 'busy'). Pass `null` to clear. */
  setManualStatus(status: UserStatus | null): void {
    this.store.dispatch(UsersActions.setManualStatus({ status }));
    this.audio.dndMuted.set(status === 'busy');
    this.broadcastStatus(this.resolveEffectiveStatus(status));
  }

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

  private cleanup(): void {
    this.electronCleanup?.();
    this.electronCleanup = null;

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

    for (const remove of this.browserActivityListeners) {
      remove();
    }

    this.browserActivityListeners = [];
    this.started = false;
  }

  private startElectronIdleDetection(): void {
    const api = this.getElectronIdleApi();

    if (!api)
      return;

    this.electronCleanup = api.onIdleStateChanged((idleState: 'active' | 'idle') => {
      this.zone.run(() => {
        this.currentAutoStatus = idleState === 'idle' ? 'away' : 'online';
        this.applyAutoStatusIfAllowed();
      });
    });

    // Check initial state
    api.getIdleState().then((idleState: 'active' | 'idle') => {
      this.zone.run(() => {
        this.currentAutoStatus = idleState === 'idle' ? 'away' : 'online';
        this.applyAutoStatusIfAllowed();
      });
    });
  }

  private startBrowserIdleDetection(): void {
    this.lastActivityTimestamp = Date.now();

    const onActivity = () => {
      this.lastActivityTimestamp = Date.now();
      const wasAway = this.currentAutoStatus === 'away';

      if (wasAway) {
        this.currentAutoStatus = 'online';
        this.zone.run(() => this.applyAutoStatusIfAllowed());
      }
    };
    const events = [
      'mousemove',
      'keydown',
      'mousedown',
      'touchstart',
      'scroll'
    ] as const;

    for (const evt of events) {
      document.addEventListener(evt, onActivity, { passive: true });
      this.browserActivityListeners.push(() =>
        document.removeEventListener(evt, onActivity)
      );
    }

    this.zone.runOutsideAngular(() => {
      this.browserPollTimer = setInterval(() => {
        const idle = Date.now() - this.lastActivityTimestamp >= BROWSER_IDLE_THRESHOLD_MS;

        if (idle && this.currentAutoStatus !== 'away') {
          this.currentAutoStatus = 'away';
          this.zone.run(() => this.applyAutoStatusIfAllowed());
        }
      }, BROWSER_IDLE_POLL_MS);
    });
  }

  private applyAutoStatusIfAllowed(): void {
    const manualStatus = this.manualStatus();

    // Manual status overrides automatic
    if (manualStatus)
      return;

    const currentUser = this.currentUser();

    if (currentUser?.status !== this.currentAutoStatus) {
      this.store.dispatch(UsersActions.setManualStatus({ status: null }));
      this.store.dispatch(UsersActions.updateCurrentUser({ updates: { status: this.currentAutoStatus } }));
      this.broadcastStatus(this.currentAutoStatus);
    }
  }

  private resolveEffectiveStatus(manualStatus: UserStatus | null): UserStatus {
    return manualStatus ?? this.currentAutoStatus;
  }

  private broadcastStatus(status: UserStatus): void {
    this.webrtc.sendRawMessage({
      type: 'status_update',
      status
    });
  }

  private getElectronIdleApi(): ElectronIdleApi | undefined {
    return (window as IdleAwareWindow).electronAPI;
  }
}
