/**
 * Ephemeral webcam-face consent state, with no media or persistence capability.
 *
 * A future browser adapter may react to an accepted `start` transition by
 * calling `getUserMedia`; this module cannot do so, cannot fetch anything, and
 * deliberately has no encode/storage API. Every fresh controller starts off.
 */

export type WebcamFaceConsentStatus =
  | "off"
  | "awaiting-consent"
  | "active"
  | "stopped"
  | "revoked";

export interface WebcamFaceConsentState {
  status: WebcamFaceConsentStatus;
  /** True only between an explicit accepted start and stop/revoke. */
  consentGranted: boolean;
  readonly ephemeral: true;
  readonly persistence: "none";
  readonly autoFetch: false;
  revision: number;
}

export type WebcamFaceConsentEvent =
  | { type: "request-start" }
  | { type: "start"; explicitConsent: true }
  | { type: "stop" }
  | { type: "revoke" };

export type WebcamFaceConsentRejection =
  | "already-awaiting"
  | "already-active"
  | "already-stopped"
  | "already-revoked"
  | "consent-required";

export type WebcamFaceConsentTransition =
  | { accepted: true; state: WebcamFaceConsentState }
  | { accepted: false; state: WebcamFaceConsentState; reason: WebcamFaceConsentRejection };

export interface WebcamFaceConsentController {
  state(): WebcamFaceConsentState;
  requestStart(): WebcamFaceConsentTransition;
  /** Only valid after `requestStart`; the literal true prevents accidental implicit consent. */
  start(explicitConsent: true): WebcamFaceConsentTransition;
  stop(): WebcamFaceConsentTransition;
  revoke(): WebcamFaceConsentTransition;
}

export function initialWebcamFaceConsent(): WebcamFaceConsentState {
  return {
    status: "off",
    consentGranted: false,
    ephemeral: true,
    persistence: "none",
    autoFetch: false,
    revision: 0,
  };
}

function copy(state: WebcamFaceConsentState): WebcamFaceConsentState {
  return { ...state };
}

function reject(
  state: WebcamFaceConsentState,
  reason: WebcamFaceConsentRejection,
): WebcamFaceConsentTransition {
  return { accepted: false, state: copy(state), reason };
}

function accept(
  state: WebcamFaceConsentState,
  status: WebcamFaceConsentStatus,
  consentGranted: boolean,
): WebcamFaceConsentTransition {
  return {
    accepted: true,
    state: { ...state, status, consentGranted, revision: state.revision + 1 },
  };
}

/** Pure transition primitive for UI stores and deterministic tests. */
export function transitionWebcamFaceConsent(
  state: WebcamFaceConsentState,
  event: WebcamFaceConsentEvent,
): WebcamFaceConsentTransition {
  switch (event.type) {
    case "request-start":
      if (state.status === "awaiting-consent") return reject(state, "already-awaiting");
      if (state.status === "active") return reject(state, "already-active");
      return accept(state, "awaiting-consent", false);
    case "start":
      if (state.status === "active") return reject(state, "already-active");
      if (state.status !== "awaiting-consent" || event.explicitConsent !== true) {
        return reject(state, "consent-required");
      }
      return accept(state, "active", true);
    case "stop":
      if (state.status === "stopped" || state.status === "off") return reject(state, "already-stopped");
      if (state.status === "revoked") return reject(state, "already-revoked");
      return accept(state, "stopped", false);
    case "revoke":
      if (state.status === "revoked") return reject(state, "already-revoked");
      return accept(state, "revoked", false);
  }
}

export function createWebcamFaceConsent(): WebcamFaceConsentController {
  let current = initialWebcamFaceConsent();
  function apply(event: WebcamFaceConsentEvent): WebcamFaceConsentTransition {
    const transition = transitionWebcamFaceConsent(current, event);
    if (transition.accepted) current = transition.state;
    return { ...transition, state: copy(transition.state) };
  }
  return {
    state: () => copy(current),
    requestStart: () => apply({ type: "request-start" }),
    start: (explicitConsent) => apply({ type: "start", explicitConsent }),
    stop: () => apply({ type: "stop" }),
    revoke: () => apply({ type: "revoke" }),
  };
}
