// LoginEvent — discriminated union the parser emits and the state machine
// consumes. One LoginEvent per inbound m.room.message / m.room.redaction
// from the bridge bot.
//
// Source-of-truth for every kind below is mautrix/discord legacy command
// system (commands.go), tag v0.7.6 — see ./dialects/legacy_v076.ts for the
// per-string upstream pointers. Discord uses the OLDER mautrix command
// processor (`maunium.net/go/mautrix/bridge/commands`), NOT bridgev2 — so
// the wording differs from mautrix-telegram and there's no list-logins
// API; status is queried via `ping`, and there's no per-account login id.

// `ping` reply variants — the bridge's only status-probe surface for the
// legacy command system. Each variant maps to a different LoginEvent so
// the state machine can render distinct status pills.
export type PingResult =
  | { kind: 'not_logged_in' }
  | { kind: 'token_stored_not_connected' }
  | { kind: 'connection_dead' }
  | { kind: 'logged_in'; handle: string; discordId?: string };

// Shape of an inbound event the dialect parser needs to look at. Matches
// the wire shape produced by the host's BotWidgetDriver sanitizer; declared
// here (not in widget-api.ts) so the dialect doesn't import from the
// transport layer.
export type ParsableEvent = {
  type: string;
  event_id: string;
  sender: string;
  origin_server_ts?: number;
  content: { msgtype?: string; body?: string; [k: string]: unknown };
  redacts?: string;
};

export type LoginEvent =
  // --- ping reply ----------------------------------------------------------
  | { kind: 'not_logged_in' }
  | { kind: 'token_stored_not_connected' }
  | { kind: 'connection_dead' }
  // `ping` says we're live; handle parsed from `You're logged in as @x (`id`)`.
  // Same shape as `login_success` — App routes both into the connected state.
  | { kind: 'logged_in'; handle: string; discordId?: string }

  // --- login-qr lifecycle --------------------------------------------------
  // `m.image` carrying the remoteauth URL inside `content.body`. The widget
  // renders the QR client-side from that URL and never touches the uploaded
  // PNG. Discord's remoteauth does NOT rotate the URL (unlike Telegram
  // MTProto): the bridge sends one m.image per login attempt and either
  // redacts it on success or leaves it (and replies with an error) on
  // failure. `replacesEventId` is here for forward-compat / paranoia — if a
  // future bridge ever does an edit, the state machine handles it gracefully.
  | { kind: 'qr_displayed'; discordUrl: string; eventId: string; replacesEventId?: string }
  // Bridge redacted the QR event after a successful scan. NOT terminal — the
  // success line («Successfully logged in as @x») typically lands in the same
  // breath; the state machine moves us into a `qr_verifying` interstitial
  // until it does.
  | { kind: 'qr_redacted'; redactsEventId: string }

  // Successful login (after QR scan). Captures handle and optional snowflake.
  | { kind: 'login_success'; handle: string; discordId?: string }
  // Generic login failure (wraps gotd / remoteauth Go errors). Most common
  // bodies: «Error logging in: ...» — we surface the verbatim Go-error tail
  // as a yellow warning on the QR panel.
  | { kind: 'login_failed'; reason?: string }
  // Vojo-patched bridge replies with a sentinel-prefixed JSON line
  // («VOJO-CAPTCHA-CHALLENGE-V1 {...}») when Discord demands an hCaptcha
  // before completing remote-auth. The widget renders a hCaptcha iframe
  // with the supplied sitekey + rqdata, then sends the solved token back
  // via `<commandPrefix> login-captcha <token>` to retry the login.
  | {
      kind: 'captcha_challenge';
      service: string;
      sitekey: string;
      sessionId: string;
      rqdata: string;
      rqtoken: string;
    }
  // Legacy «CAPTCHAs are currently not supported - use token login instead»
  // path — only fires against an UNPATCHED upstream v0.7.6 bridge. Kept so
  // a deployment that forgets to ship the Vojo bridge image still surfaces
  // a useful hint instead of a generic Go-error tail.
  | { kind: 'captcha_required' }
  // bridge sets up a websocket against Discord's remoteauth gateway; this is
  // the «we couldn't even reach Discord» error — different from
  // login_failed, which lands AFTER the websocket is up.
  | { kind: 'login_websocket_failed'; reason?: string }
  // Surfaces when QR-login starts but the bridge is already logged in.
  // Race against ping/status — the App fires `ping` to reconcile.
  | { kind: 'already_logged_in' }
  // bridge couldn't initialise remoteauth at all (rare, indicates bridge-
  // image misconfiguration). Routes back to disconnected with a warn line.
  | { kind: 'prepare_login_failed'; reason?: string }
  // bridge received the token but couldn't connect to Discord with it (rare
  // post-scan failure; remoteauth can return a stale token if the gateway
  // race trips). Surfaces as «Signed in, but couldn't connect: <reason>»
  // and routes back to disconnected.
  | { kind: 'connect_after_login_failed'; reason?: string }

  // --- logout --------------------------------------------------------------
  | { kind: 'logout_ok' }
  // Bridge says the user wasn't logged in but cleared state defensively.
  // Idempotent confirmation that we're now disconnected.
  | { kind: 'logout_no_op' }

  // --- disconnect / reconnect ---------------------------------------------
  // Used as recovery from `connection_dead` / `token_stored_not_connected`.
  // The widget never SENDS `disconnect` — that's an admin-only state op —
  // but if the user typed it manually in chat-fallback, the parser still
  // recognises the reply.
  | { kind: 'disconnect_ok' }
  | { kind: 'disconnect_no_op' }
  | { kind: 'disconnect_failed'; reason?: string }
  | { kind: 'reconnect_ok' }
  | { kind: 'reconnect_no_op' }
  | { kind: 'reconnect_failed'; reason?: string }

  // --- Vojo: bridge-managed personal space ---------------------------------
  // Vojo-patched bridge emits the sentinel `VOJO-LOGIN-SPACE-V1 {...}` as a
  // separate m.notice right after the «Successfully logged in» line. Carries
  // a `matrix.to` URL pointing at the user's auto-created Discord space
  // (user.go::GetSpaceRoom on the bridge side). The widget surfaces this as
  // an «Open in Channels» card; click → host navigates cinny to the space.
  // See vojo-mautrix-discord/commands_login_space.go for the wire format.
  | { kind: 'space_ready'; matrixToUrl: string }

  // --- bridge-side errors --------------------------------------------------
  // Generic «I don't know that command» — should not happen since we only
  // ship known commands, but visible if the bridge image is misconfigured
  // or the prefix in /config.json drifted from the bridge's command_prefix.
  | { kind: 'unknown_command' }
  | { kind: 'unknown' };
