import type { ServerState } from '../types/state';
import type { Capabilities } from '../types/capabilities';
import type { InfoResponse } from '../types/protocol';
import { markStateUpdated, setHttpConnected, setRadioStatus, setReconnecting } from '../stores/connection.svelte';

const BASE = '/api/v1';

function getStoredToken(): string | null {
  const storage = globalThis.localStorage;
  if (!storage || typeof storage.getItem !== 'function') {
    return null;
  }
  return storage.getItem('icom-lan-auth-token');
}

function getAuthHeaders(): Record<string, string> {
  const token = getStoredToken();
  if (token) return { Authorization: `Bearer ${token}` };
  return {};
}

function handleUnauthorized(): void {
  const token = prompt('Enter auth token:');
  if (token) {
    localStorage.setItem('icom-lan-auth-token', token);
    location.reload();
  }
}

export async function fetchState(): Promise<ServerState> {
  const res = await fetch(`${BASE}/state`, { headers: getAuthHeaders() });
  if (res.status === 401) { handleUnauthorized(); throw new Error('Unauthorized'); }
  if (!res.ok) throw new Error(`fetchState: ${res.status}`);
  return res.json() as Promise<ServerState>;
}

export async function fetchCapabilities(): Promise<Capabilities> {
  const res = await fetch(`${BASE}/capabilities`, { headers: getAuthHeaders() });
  if (res.status === 401) { handleUnauthorized(); throw new Error('Unauthorized'); }
  if (!res.ok) throw new Error(`fetchCapabilities: ${res.status}`);
  return res.json() as Promise<Capabilities>;
}

/** Fetch server info (version, uptime). Used by StatusBar component (Sprint 2). */
export async function fetchInfo(): Promise<InfoResponse> {
  const res = await fetch(`${BASE}/info`, { headers: getAuthHeaders() });
  if (res.status === 401) { handleUnauthorized(); throw new Error('Unauthorized'); }
  if (!res.ok) throw new Error(`fetchInfo: ${res.status}`);
  return res.json() as Promise<InfoResponse>;
}

/**
 * Poll `/api/v1/state` at the given interval, calling `callback` only when
 * the revision advances. Skips the poll if the previous one hasn't returned.
 *
 * @returns A stop function.
 */
const HTTP_ERROR_THRESHOLD = 3;

export function startPolling(
  callback: (state: ServerState) => void,
  intervalMs = 200,
): () => void {
  let timer: ReturnType<typeof setTimeout> | null = null;
  let running = true;
  let inflight = false;
  let lastRevision = -1;
  let consecutiveErrors = 0;

  async function tick() {
    if (!running) return;
    if (!inflight) {
      inflight = true;
      try {
        const state = await fetchState();
        consecutiveErrors = 0;
        setReconnecting(false);
        setHttpConnected(true);
        markStateUpdated();
        if (state.radioDetail?.status) {
          setRadioStatus(state.radioDetail.status);
        }
        if (state.revision > lastRevision) {
          lastRevision = state.revision;
          callback(state);
        }
      } catch {
        consecutiveErrors++;
        setReconnecting(true);
        if (consecutiveErrors >= HTTP_ERROR_THRESHOLD) {
          setHttpConnected(false);
        }
      } finally {
        inflight = false;
      }
    }
    if (running) {
      timer = setTimeout(tick, intervalMs);
    }
  }

  void tick();

  return () => {
    running = false;
    setReconnecting(false);
    if (timer) clearTimeout(timer);
  };
}
