import { describe, expect, it } from 'vitest';
import {
  PIN_FLOW_POLL_BUDGET_MS,
  PIN_FLOW_POLL_INTERVAL_MS,
  evaluatePinFlow,
  initialPinFlowState,
  isPinFlowBudgetExhausted
} from './pinFlowPoll';

describe('pinFlowPoll constants', () => {
  it('polls every 2s for up to 150s (design §C1)', () => {
    expect(PIN_FLOW_POLL_INTERVAL_MS).toBe(2000);
    expect(PIN_FLOW_POLL_BUDGET_MS).toBe(150_000);
  });
});

describe('initialPinFlowState', () => {
  it('starts in the non-terminal waiting state', () => {
    const state = initialPinFlowState();
    expect(state.status).toBe('waiting');
    expect(state.done).toBe(false);
    expect(state.ok).toBe(false);
  });
});

describe('evaluatePinFlow — all transitions with injected state', () => {
  it('locked & !authorized => waiting (keep polling)', () => {
    const state = evaluatePinFlow({ isLocked: true, isAuthorized: false, hasServers: false });
    expect(state.status).toBe('waiting');
    expect(state.done).toBe(false);
    expect(state.ok).toBe(false);
  });

  it('locked & authorized => finalizing (keep polling, discovering servers)', () => {
    const state = evaluatePinFlow({ isLocked: true, isAuthorized: true, hasServers: false });
    expect(state.status).toBe('finalizing');
    expect(state.done).toBe(false);
    expect(state.ok).toBe(false);
  });

  it('!locked & authorized & servers => success (terminal)', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: true, hasServers: true });
    expect(state.status).toBe('success');
    expect(state.done).toBe(true);
    expect(state.ok).toBe(true);
  });

  it('!locked & authorized & NO servers => authorized-no-servers (terminal, recoverable, not ok) — #345', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: true, hasServers: false });
    expect(state.status).toBe('authorized-no-servers');
    expect(state.done).toBe(true); // terminal: the lock has released, polling stops
    expect(state.ok).toBe(false); // authorized, but NOT "Connected to Plex."
    expect(state.message).not.toBe('Connected to Plex.');
    expect(state.message).toMatch(/no eligible servers/i);
  });

  it('!locked & !authorized => timeout (terminal, ended without auth)', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: false, hasServers: false });
    expect(state.status).toBe('timeout');
    expect(state.done).toBe(true);
    expect(state.ok).toBe(false);
  });

  it('locked & budget exhausted => budget-exhausted (terminal, not a failure)', () => {
    const state = evaluatePinFlow({ isLocked: true, isAuthorized: true, hasServers: false }, { budgetExhausted: true });
    expect(state.status).toBe('budget-exhausted');
    expect(state.done).toBe(true);
    expect(state.ok).toBe(false);
  });

  it('budget exhaustion is ignored once the lock has released (success still wins)', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: true, hasServers: true }, { budgetExhausted: true });
    expect(state.status).toBe('success');
    expect(state.ok).toBe(true);
  });

  it('budget exhaustion is ignored once the lock has released (authorized-no-servers still wins)', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: true, hasServers: false }, { budgetExhausted: true });
    expect(state.status).toBe('authorized-no-servers');
    expect(state.ok).toBe(false);
  });

  it('budget exhaustion is ignored once the lock has released (timeout still wins)', () => {
    const state = evaluatePinFlow({ isLocked: false, isAuthorized: false, hasServers: false }, { budgetExhausted: true });
    expect(state.status).toBe('timeout');
  });
});

describe('isPinFlowBudgetExhausted', () => {
  it('is false until the budget is reached and true at/after it', () => {
    expect(isPinFlowBudgetExhausted(0)).toBe(false);
    expect(isPinFlowBudgetExhausted(PIN_FLOW_POLL_BUDGET_MS - 1)).toBe(false);
    expect(isPinFlowBudgetExhausted(PIN_FLOW_POLL_BUDGET_MS)).toBe(true);
    expect(isPinFlowBudgetExhausted(PIN_FLOW_POLL_BUDGET_MS + 5000)).toBe(true);
  });
});
