/**
 * JSON-safe signaling contracts for an eventual server-authoritative office
 * screen-share service. These types intentionally contain no browser or WebRTC
 * runtime objects and no MediaSurfaceSource locator.
 */

export const SCREEN_SHARE_SIGNALING_PROTOCOL_VERSION = 1 as const;
/** One presenter plus at most seven viewers in the initial bounded P2P mesh. */
export const MAX_SCREEN_SHARE_PARTICIPANTS = 8 as const;

export type ScreenShareRole = "presenter" | "viewer";
export type ScreenShareSequence = number;
export type ScreenShareTimestamp = number;

/** The authored office surface to which a signaling session is bound. */
export interface ScreenShareBinding {
  officeId: string;
  levelId: string;
  roomId: string | null;
  screenId: string;
}

/**
 * Private bearer material. It may cross the client/server boundary but must
 * never be copied into a ScreenSharePeerMessage.
 */
export interface ScreenShareCredential<R extends ScreenShareRole = ScreenShareRole> {
  sessionId: string;
  participantId: string;
  role: R;
  grantToken: string;
}

export interface ScreenShareAccessGrant<R extends ScreenShareRole = ScreenShareRole> {
  credential: ScreenShareCredential<R>;
  issuedAtMs: ScreenShareTimestamp;
  expiresAtMs: ScreenShareTimestamp;
}

/** Server-issued, session-scoped identity; never an account/profile identifier. */
export interface ScreenShareParticipant {
  participantId: string;
  role: ScreenShareRole;
}

export interface ScreenShareSdpSignal {
  kind: "sdp";
  descriptionType: "offer" | "answer";
  sdp: string;
}

export interface ScreenShareIceCandidateSignal {
  kind: "ice-candidate";
  candidate: string;
  sdpMid: string | null;
  sdpMLineIndex: number | null;
  usernameFragment: string | null;
}

export interface ScreenShareIceCompleteSignal {
  kind: "ice-complete";
}

export type ScreenShareSignalPayload =
  | ScreenShareSdpSignal
  | ScreenShareIceCandidateSignal
  | ScreenShareIceCompleteSignal;

interface ScreenShareEnvelope {
  protocolVersion: typeof SCREEN_SHARE_SIGNALING_PROTOCOL_VERSION;
  sequence: ScreenShareSequence;
  timestampMs: ScreenShareTimestamp;
}

interface ScreenShareRequestedEnvelope extends ScreenShareEnvelope {
  requestId: string;
}

export interface ScreenShareCreateRequest extends ScreenShareRequestedEnvelope {
  type: "screen-share-create-request";
  binding: ScreenShareBinding;
  role: "presenter";
}

export interface ScreenShareJoinRequest extends ScreenShareRequestedEnvelope {
  type: "screen-share-join-request";
  binding: ScreenShareBinding;
  role: "viewer";
  /** A literal, affirmative action; omission and false are both invalid. */
  viewerOptIn: true;
}

export interface ScreenShareResumeRequest extends ScreenShareRequestedEnvelope {
  type: "screen-share-resume-request";
  binding: ScreenShareBinding;
  credential: ScreenShareCredential;
  lastReceivedSequence: ScreenShareSequence;
}

export interface ScreenShareSignalRequest extends ScreenShareEnvelope {
  type: "screen-share-signal-request";
  binding: ScreenShareBinding;
  credential: ScreenShareCredential;
  targetParticipantId: string;
  signal: ScreenShareSignalPayload;
}

export type ScreenShareClientStopReason = "presenter-stopped" | "capture-ended";

export interface ScreenShareStopRequest extends ScreenShareRequestedEnvelope {
  type: "screen-share-stop-request";
  binding: ScreenShareBinding;
  credential: ScreenShareCredential<"presenter">;
  reason: ScreenShareClientStopReason;
}

export type ScreenShareRevokeScope = "session" | "participant";
export type ScreenShareRevokeRequestReason =
  | "viewer-left"
  | "presenter-removed-viewer"
  | "moderator-action";

export interface ScreenShareRevokeRequest extends ScreenShareRequestedEnvelope {
  type: "screen-share-revoke-request";
  binding: ScreenShareBinding;
  credential: ScreenShareCredential;
  scope: ScreenShareRevokeScope;
  targetParticipantId: string | null;
  reason: ScreenShareRevokeRequestReason;
}

interface ScreenShareGrantEnvelope<R extends ScreenShareRole> extends ScreenShareRequestedEnvelope {
  binding: ScreenShareBinding;
  grant: ScreenShareAccessGrant<R>;
  nextClientSequence: ScreenShareSequence;
  /** Authorized peers excluding the grant recipient. */
  participants: readonly ScreenShareParticipant[];
}

export interface ScreenShareCreateGrant extends ScreenShareGrantEnvelope<"presenter"> {
  type: "screen-share-create-grant";
}

export interface ScreenShareJoinGrant extends ScreenShareGrantEnvelope<"viewer"> {
  type: "screen-share-join-grant";
}

export interface ScreenShareResumeGrant extends ScreenShareGrantEnvelope<ScreenShareRole> {
  type: "screen-share-resume-grant";
  /** False requires a fresh negotiation; no missed relays are implied. */
  continuous: boolean;
}

/**
 * Current authorized peers. Clients reconcile their P2P mesh to this complete
 * list and close any connection absent from it.
 */
export interface ScreenShareParticipants extends ScreenShareEnvelope {
  type: "screen-share-participants";
  sessionId: string;
  binding: ScreenShareBinding;
  participants: readonly ScreenShareParticipant[];
  leaseExpiresAtMs: ScreenShareTimestamp;
}

/** Credential-free message safe to deliver to one explicitly opted-in peer. */
export interface ScreenShareSignalRelay extends ScreenShareEnvelope {
  type: "screen-share-signal-relay";
  sessionId: string;
  binding: ScreenShareBinding;
  fromParticipantId: string;
  targetParticipantId: string;
  signal: ScreenShareSignalPayload;
}

export type ScreenShareStoppedReason =
  | ScreenShareClientStopReason
  | "grant-expired"
  | "server-shutdown";

/** Credential-free terminal notice safe to deliver to session peers. */
export interface ScreenShareStopped extends ScreenShareEnvelope {
  type: "screen-share-stopped";
  sessionId: string;
  binding: ScreenShareBinding;
  reason: ScreenShareStoppedReason;
}

export type ScreenShareRevocationReason =
  | "viewer-left"
  | "presenter-removed-viewer"
  | "moderator-action"
  | "authorization-revoked"
  | "membership-revoked"
  | "protocol-violation"
  | "session-replaced";

/** Credential-free revocation notice; participant ids are session-opaque. */
export interface ScreenShareRevoked extends ScreenShareEnvelope {
  type: "screen-share-revoked";
  sessionId: string;
  binding: ScreenShareBinding;
  scope: ScreenShareRevokeScope;
  targetParticipantId: string | null;
  reason: ScreenShareRevocationReason;
  reconnectAllowed: boolean;
}

export type ScreenShareClientMessage =
  | ScreenShareCreateRequest
  | ScreenShareJoinRequest
  | ScreenShareResumeRequest
  | ScreenShareSignalRequest
  | ScreenShareStopRequest
  | ScreenShareRevokeRequest;

export type ScreenShareServerMessage =
  | ScreenShareCreateGrant
  | ScreenShareJoinGrant
  | ScreenShareResumeGrant
  | ScreenShareParticipants
  | ScreenShareSignalRelay
  | ScreenShareStopped
  | ScreenShareRevoked;

/** The only server messages permitted on a peer fan-out channel. */
export type ScreenSharePeerMessage =
  | ScreenShareParticipants
  | ScreenShareSignalRelay
  | ScreenShareStopped
  | ScreenShareRevoked;

export type ScreenShareSessionMessage =
  | ScreenShareResumeRequest
  | ScreenShareSignalRequest
  | ScreenShareStopRequest
  | ScreenShareRevokeRequest
  | ScreenShareParticipants
  | ScreenShareSignalRelay
  | ScreenShareStopped
  | ScreenShareRevoked;

export interface ScreenShareStreamCursor {
  sessionId: string;
  sequence: ScreenShareSequence;
  timestampMs: ScreenShareTimestamp;
}

export type ScreenShareValidationResult<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };
