import type { Message, Reaction } from './message.models';
import type { UserRole, RoomMember } from './user.models';
import type {
  Room,
  RoomSettings,
  RoomPermissions,
  Channel
} from './room.models';
import type { VoiceState } from './voice-state.models';
import type { GameActivity } from './game-activity.models';
import type { BanEntry } from './moderation.models';
import type { ChatAttachmentAnnouncement, ChatAttachmentMeta } from './attachment-contracts';
import type {
  DirectMessageEventPayload,
  DirectMessageMutationEventPayload,
  DirectMessageStatusEventPayload
} from './direct-message-contracts';

export interface ChatInventoryItem {
  id: string;
  ts: number;
  rc: number;
  ac?: number;
}

// Every field that appears on any event is kept optional here so that
// code which accesses `event.roomId` etc. without narrowing first
// still compiles.  Individual event interfaces below then make the
// relevant fields **required**.

export interface ChatEventBase {
  fromPeerId?: string;
  messageId?: string;
  message?: Message;
  reaction?: Reaction;
  data?: string | Partial<Message>;
  timestamp?: number;
  targetUserId?: string;
  roomId?: string;
  items?: ChatInventoryItem[];
  ids?: string[];
  messages?: Message[];
  attachments?: Record<string, ChatAttachmentMeta[]>;
  total?: number;
  index?: number;
  count?: number;
  lastUpdated?: number;
  file?: ChatAttachmentAnnouncement;
  fileId?: string;
  username?: string;
  hostId?: string;
  hostOderId?: string;
  previousHostId?: string;
  previousHostOderId?: string;
  kickedBy?: string;
  bannedBy?: string;
  content?: string;
  editedAt?: number;
  deletedAt?: number;
  deletedBy?: string;
  oderId?: string;
  displayName?: string;
  description?: string;
  profileUpdatedAt?: number;
  emoji?: string;
  reason?: string;
  settings?: Partial<RoomSettings>;
  permissions?: Partial<RoomPermissions>;
  voiceState?: Partial<VoiceState>;
  gameActivity?: GameActivity | null;
  isScreenSharing?: boolean;
  isCameraEnabled?: boolean;
  icon?: string;
  iconUpdatedAt?: number;
  avatarUrl?: string;
  avatarHash?: string;
  avatarMime?: string;
  avatarUpdatedAt?: number;
  role?: UserRole;
  room?: Partial<Room>;
  channels?: Channel[];
  members?: RoomMember[];
  ban?: BanEntry;
  bans?: BanEntry[];
  banOderId?: string;
  expiresAt?: number;
  directMessage?: DirectMessageEventPayload;
  directMessageStatus?: DirectMessageStatusEventPayload;
  directMessageMutation?: DirectMessageMutationEventPayload;
  pluginMessage?: unknown;
}

export interface ChatMessageEvent extends ChatEventBase {
  type: 'message' | 'chat-message';
  message: Message;
}

export interface MessageEditedEvent extends ChatEventBase {
  type: 'edit' | 'message-edited';
  messageId: string;
  content: string;
  editedAt: number;
}

export interface MessageDeletedEvent extends ChatEventBase {
  type: 'delete' | 'message-deleted';
  messageId: string;
}

export interface ReactionAddedEvent extends ChatEventBase {
  type: 'reaction' | 'reaction-added';
  messageId: string;
  reaction: Reaction;
}

export interface ReactionRemovedEvent extends ChatEventBase {
  type: 'reaction-removed';
  messageId: string;
  oderId: string;
  emoji: string;
}

export interface FileAnnounceChatEvent extends ChatEventBase {
  type: 'file-announce';
  messageId: string;
  file: ChatAttachmentAnnouncement;
}

export interface FileChunkChatEvent extends ChatEventBase {
  type: 'file-chunk';
  messageId: string;
  fileId: string;
  index: number;
  total: number;
  data: string;
}

export interface FileRequestChatEvent extends ChatEventBase {
  type: 'file-request';
  messageId: string;
  fileId: string;
}

export interface FileCancelChatEvent extends ChatEventBase {
  type: 'file-cancel';
  messageId: string;
  fileId: string;
}

export interface FileNotFoundChatEvent extends ChatEventBase {
  type: 'file-not-found';
  messageId: string;
  fileId: string;
}

export interface ChatInventoryRequestEvent extends ChatEventBase {
  type: 'chat-inventory-request';
  roomId: string;
}

export interface ChatInventoryEvent extends ChatEventBase {
  type: 'chat-inventory';
  roomId: string;
  items: ChatInventoryItem[];
  total: number;
}

export interface ChatSyncRequestIdsEvent extends ChatEventBase {
  type: 'chat-sync-request-ids';
  roomId: string;
  ids: string[];
}

export interface ChatSyncBatchEvent extends ChatEventBase {
  type: 'chat-sync-batch';
  roomId: string;
  messages: Message[];
}

export interface ChatSyncSummaryEvent extends ChatEventBase {
  type: 'chat-sync-summary';
}

export interface ChatSyncRequestEvent extends ChatEventBase {
  type: 'chat-sync-request';
  roomId: string;
}

export interface ChatSyncFullEvent extends ChatEventBase {
  type: 'chat-sync-full';
  roomId: string;
  messages: Message[];
}

export interface RoomDeletedEvent extends ChatEventBase {
  type: 'room-deleted';
  roomId: string;
}

export interface RoomSettingsUpdateEvent extends ChatEventBase {
  type: 'room-settings-update';
  roomId: string;
  settings: Partial<RoomSettings>;
}

export interface RoomPermissionsUpdateEvent extends ChatEventBase {
  type: 'room-permissions-update';
  roomId: string;
  permissions?: Partial<RoomPermissions>;
}

export interface HostChangeEvent extends ChatEventBase {
  type: 'host-change';
  roomId: string;
  hostId: string;
  hostOderId: string;
  previousHostId: string;
  previousHostOderId: string;
}

export interface VoiceStateEvent extends ChatEventBase {
  type: 'voice-state';
  voiceState: Partial<VoiceState>;
}

export interface VoiceChannelMoveEvent extends ChatEventBase {
  type: 'voice-channel-move';
  roomId: string;
  targetUserId: string;
  voiceState: Partial<VoiceState>;
}

export interface ScreenStateEvent extends ChatEventBase {
  type: 'screen-state';
  isScreenSharing: boolean;
}

export interface CameraStateEvent extends ChatEventBase {
  type: 'camera-state';
  isCameraEnabled: boolean;
}

export interface GameActivityEvent extends ChatEventBase {
  type: 'game-activity';
  gameActivity: GameActivity | null;
}

export interface VoiceStateRequestEvent extends ChatEventBase {
  type: 'voice-state-request';
}

export interface StateRequestEvent extends ChatEventBase {
  type: 'state-request';
}

export interface ScreenShareRequestEvent extends ChatEventBase {
  type: 'screen-share-request';
}

export interface ScreenShareStopEvent extends ChatEventBase {
  type: 'screen-share-stop';
}

export interface ServerIconSummaryEvent extends ChatEventBase {
  type: 'server-icon-summary';
  roomId: string;
  iconUpdatedAt: number;
}

export interface ServerIconRequestEvent extends ChatEventBase {
  type: 'server-icon-request';
  roomId: string;
}

export interface ServerIconFullEvent extends ChatEventBase {
  type: 'server-icon-full';
  roomId: string;
  icon?: string;
  iconUpdatedAt: number;
}

export interface ServerIconUpdateEvent extends ChatEventBase {
  type: 'server-icon-update';
  roomId: string;
  icon: string;
  iconUpdatedAt: number;
}

export interface UserAvatarSummaryEvent extends ChatEventBase {
  type: 'user-avatar-summary';
  oderId: string;
  username?: string;
  displayName?: string;
  description?: string;
  profileUpdatedAt?: number;
  avatarHash?: string;
  avatarMime?: string;
  avatarUpdatedAt: number;
}

export interface UserAvatarRequestEvent extends ChatEventBase {
  type: 'user-avatar-request';
  oderId: string;
}

export interface UserAvatarFullEvent extends ChatEventBase {
  type: 'user-avatar-full';
  oderId: string;
  username?: string;
  displayName?: string;
  description?: string;
  profileUpdatedAt?: number;
  avatarHash?: string;
  avatarMime?: string;
  avatarUpdatedAt: number;
  total: number;
}

export interface UserAvatarChunkEvent extends ChatEventBase {
  type: 'user-avatar-chunk';
  oderId: string;
  avatarHash?: string;
  avatarMime?: string;
  avatarUpdatedAt?: number;
  index: number;
  total: number;
  data: string;
}

export interface ServerStateRequestEvent extends ChatEventBase {
  type: 'server-state-request';
  roomId: string;
}

export interface ServerStateFullEvent extends ChatEventBase {
  type: 'server-state-full';
  roomId: string;
  room: Partial<Room>;
}

export interface MemberRosterRequestEvent extends ChatEventBase {
  type: 'member-roster-request';
  roomId: string;
}

export interface MemberRosterEvent extends ChatEventBase {
  type: 'member-roster';
  roomId: string;
  members: RoomMember[];
}

export interface MemberLeaveEvent extends ChatEventBase {
  type: 'member-leave';
  roomId: string;
}

export interface RoleChangeEvent extends ChatEventBase {
  type: 'role-change';
}

export interface KickEvent extends ChatEventBase {
  type: 'kick';
}

export interface BanEvent extends ChatEventBase {
  type: 'ban';
}

export interface UnbanEvent extends ChatEventBase {
  type: 'unban';
}

export interface ChannelsUpdateEvent extends ChatEventBase {
  type: 'channels-update';
  roomId: string;
  channels: Channel[];
}

export interface DirectMessagePeerEvent extends ChatEventBase {
  type: 'direct-message';
  directMessage: DirectMessageEventPayload;
}

export interface DirectMessageStatusPeerEvent extends ChatEventBase {
  type: 'direct-message-status';
  directMessageStatus: DirectMessageStatusEventPayload;
}

export interface DirectMessageMutationPeerEvent extends ChatEventBase {
  type: 'direct-message-mutation';
  directMessageMutation: DirectMessageMutationEventPayload;
}

export interface PluginMessageBusPeerEvent extends ChatEventBase {
  type: 'plugin-message-bus';
  pluginMessage: unknown;
}

/** Discriminated union of all P2P chat events. Narrow via `event.type`. */
export type ChatEvent =
  | ChatMessageEvent
  | MessageEditedEvent
  | MessageDeletedEvent
  | ReactionAddedEvent
  | ReactionRemovedEvent
  | FileAnnounceChatEvent
  | FileChunkChatEvent
  | FileRequestChatEvent
  | FileCancelChatEvent
  | FileNotFoundChatEvent
  | ChatInventoryRequestEvent
  | ChatInventoryEvent
  | ChatSyncRequestIdsEvent
  | ChatSyncBatchEvent
  | ChatSyncSummaryEvent
  | ChatSyncRequestEvent
  | ChatSyncFullEvent
  | RoomDeletedEvent
  | RoomSettingsUpdateEvent
  | RoomPermissionsUpdateEvent
  | HostChangeEvent
  | VoiceStateEvent
  | VoiceChannelMoveEvent
  | ScreenStateEvent
  | CameraStateEvent
  | GameActivityEvent
  | VoiceStateRequestEvent
  | StateRequestEvent
  | ScreenShareRequestEvent
  | ScreenShareStopEvent
  | UserAvatarSummaryEvent
  | UserAvatarRequestEvent
  | UserAvatarFullEvent
  | UserAvatarChunkEvent
  | ServerIconSummaryEvent
  | ServerIconRequestEvent
  | ServerIconFullEvent
  | ServerIconUpdateEvent
  | ServerStateRequestEvent
  | ServerStateFullEvent
  | MemberRosterRequestEvent
  | MemberRosterEvent
  | MemberLeaveEvent
  | RoleChangeEvent
  | KickEvent
  | BanEvent
  | UnbanEvent
  | ChannelsUpdateEvent
  | DirectMessagePeerEvent
  | DirectMessageStatusPeerEvent
  | DirectMessageMutationPeerEvent
  | PluginMessageBusPeerEvent;

/** All possible `type` values, derived from the union. */
export type ChatEventType = ChatEvent['type'];
