import { type CallingState } from '@stream-io/video-react-sdk';
import { type AxiosResponse } from 'axios';
import { z } from 'zod';

import { type AvatarProps } from '@material-hu/components/design-system/Avatar';

import { type StreamCaptionsLanguages } from 'src/pages/dashboard/liveStream/constants';
import { type BROADCAST_CHANNEL_EVENTS } from 'src/pages/dashboard/videoCall/constants';

import { type Comment, type PostCommentPayload } from './comments';
import { type EventPostType } from './events';
import { type GroupCommentList, type GroupPost } from './groups';
import { type Icon } from './icons';
import { type Post, type PostData } from './posts';
import { type CursorPagination } from './services';

export enum StreamStatus {
  Idle,
  Preparing,
  Connecting,
  Reconnecting,
  Failed,
  Streaming,
  Finished,
  // Used by the back-end on the player's side.
  LIVE = 'LIVE',
  WAITING_RECORDING = 'WAITING_RECORDING',
  ENDED = 'ENDED',
}

export enum Configuration {
  GENERAL = 'GENERAL',
  CONFIGURATION = 'CONFIGURATION',
}

export enum HostInteractionTab {
  COMMENTS = 'COMMENTS',
  POLLS = 'POLLS',
}

export enum StreamSource {
  EXTERNAL = 'external',
  WEBCAM = 'webcam',
}

export type LiveStream = {
  id: string;
  providerStreamId: string;
  thumbnailUrl: string | null;
  startedAt: Date;
  status: StreamStatus;
  rtmpUrl?: string;
  rtmpKey?: string;
  captionsEnabled: boolean;
  hlsEnabled: boolean;
  hlsUrl?: string;
  recordingEnabled: boolean;
  source: SourceType;
  captionsLanguage: (typeof StreamCaptionsLanguages)[number];
  streamSource: StreamSource | null;
};

export type LiveStreamPost = Pick<
  Post,
  | 'user'
  | 'updatedAt'
  | 'createdAt'
  | 'reactions'
  | 'id'
  | 'commentCount'
  | 'commentsEnabled'
  | 'body'
  | 'bodyAttributes'
  | 'bodyHtml'
> & {
  group?: { id: string | number; title?: string };
  event?: { id: number; title?: string };
  poll?: Poll | null;
};

export type LiveStreamConfig = {
  captionsEnabled?: boolean;
  hlsEnabled?: boolean;
  captionsLanguage?: string;
};

export type LiveStreamViewerReport = {
  id: string;
  fullName: string;
  profilePictureUrl: string | null;
  employeeInternalId: string;
  userId: number;
  viewingTimeSeconds?: number;
  joinedAt?: string;
  leftAt?: string;
  role?: string;
};

export type LiveStreamStats = {
  id: string;
  startedAt: string;
  endedAt: string;
  totalViewers: number;
  totalUniqueViewers: number;
  maximumConcurrentViewers: number;
  averageViewingTimeSeconds: number;
  viewers: {
    mobile: number;
    web: number;
  };
};

export type LiveStreamTimeline = {
  amountOfViewers: number[];
  interval: number;
};

export type JoinLivestreamResponse = {
  ticketId: string;
};

export type InitializationConfig = {
  cameraEnabled: boolean;
  mobileAudio?: string;
};

export type Participant = {
  id: number;
  name: string;
  fullName: string;
  profilePicture: string | null;
  isCalling: boolean;
};

export type ParticipantList = {
  character: string;
  participants: Participant[];
};

export type CallEvent = {
  id: string;
  providerCallId: string;
  metadata: {
    channelId: string;
  };
};

export enum StatusVideoCall {
  PENDING = 'PENDING',
  ACTIVE = 'ACTIVE',
  ENDED = 'ENDED',
}

export type CurrentSession = {
  status: StatusVideoCall;
  endReason: string | null;
};

export type VideoCall = {
  id: string;
  providerCallId: string;
  initializationConfig: InitializationConfig;
  participants: Participant[];
  creator: Participant;
  currentSession: CurrentSession;
  maxParticipantsAllowed: number;
};

export type InitCallProperties = Pick<
  VideoCall,
  'id' | 'providerCallId' | 'initializationConfig' | 'creator'
> & {
  joinedUserNames?: string[];
  isGroupCall?: boolean;
};

export type CreateVideoCallRequest = {
  chatId: string | number;
  initializationConfig: InitializationConfig;
};

export type CreateMartyVideoCallRequest = {
  channelId: string;
  initializationConfig: InitializationConfig;
};

export type MartyVideoCallResponse = {
  id: string;
  initialization_config: { camera_enabled: boolean };
  provider_call_id: string;
};

export type InviteParticipantsRequest = {
  id: string;
  participantUserIds: number[];
};

export type GetParticipantsRequest = {
  id: string;
  q?: string;
  limit?: number;
  cursor?: string;
};

export type CallInitialized = {
  creator: Participant;
  id: string;
  initializationConfig: InitializationConfig;
  providerCallId: string;
  isGroupCall: boolean;
  notificationName: string;
  notificationPicture: string;
};

export type CallJoinRequested = {
  inviter: {
    fullName: string;
    profilePicture: string | null;
    userId: number;
  };
  id: string;
  providerCallId: string;
  joinedUserNames: string[];
  initializationConfig: InitializationConfig;
};

export type CallRefreshParticipants = {
  id: string;
  providerCallId: string;
};

export type ParticipantLeft = {
  id: string;
  leaverFullName: string;
  providerCallId: string;
};

export enum ResponseErrorCodes {
  LIVE_STREAM_STATS_NOT_READY = 'LIVE_STREAM_STATS_NOT_READY',
  CALL_INVALID_STATUS = 'CALL_INVALID_STATUS',
  CALL_TOO_MANY_PARTICIPANTS = 'CALL_TOO_MANY_PARTICIPANTS',
  FINISHED_CALL = 'FINISHED_CALL',
}

export type CallError =
  | ResponseErrorCodes.CALL_INVALID_STATUS
  | ResponseErrorCodes.CALL_TOO_MANY_PARTICIPANTS
  | ResponseErrorCodes.FINISHED_CALL
  | CallingState.LEFT;

export type CallBroadcastChannelMessage = {
  type: keyof typeof BROADCAST_CHANNEL_EVENTS;
  callId: string;
};

export enum VideoOrigin {
  WEBCAM = 'WEBCAM',
  SOFTWARE_STREAMING = 'SOFTWARE_STREAMING',
}

export enum UserTypeStream {
  VIEWER = 'viewer',
  HOST = 'host',
  CALL = 'calluser',
  RTMP = 'rtmp',
}

export enum CopyInputStream {
  STREAM_KEY = 'STREAM_KEY',
  STREAM_SERVER = 'STREAM_SERVER',
}

export type BytesSample = {
  totalBytes: number;
  timestamp: number;
};

export type MetricsSnapshot = {
  videoBytes: number;
  audioBytes: number;
  timestamp: number;
  frameRate?: number;
  frameHeight?: number;
  audioCodec?: string;
};

export type RateHistories = {
  videoBitrate: number[];
  frameRate: number[];
  audioBitrate: number[];
};

export type StreamRateMetric = {
  /** Latest value formatted with units (e.g. `10 Mbps`, `60 fps`), null until available */
  formattedValue: string | null;
  /** Rolling history of raw values feeding the sparkline */
  history: number[];
};

export type StreamTimeTick = {
  /** Elapsed-minutes label (e.g. `0m`, `1m`) */
  label: string;
  /** Horizontal position within the sparkline, 0 (left) to 1 (right) */
  fraction: number;
};

export enum StreamEvents {
  PARTICIPANT_JOINED = 'call.session_participant_joined',
  PARTICIPANT_LEFT = 'call.session_participant_left',
  CLOSED_CAPTION = 'call.closed_caption',
}

export enum ShareScreenStatus {
  ENABLED = 'enabled',
  DISABLED = 'disabled',
}

export enum TrackType {
  SCREEN_SHARE_TRACK = 'screenShareTrack',
  VIDEO_TRACK = 'videoTrack',
}

export enum DeviceType {
  MICROPHONE = 'MICROPHONE',
  CAMERA = 'CAMERA',
}

export enum SourceType {
  WEB = 'web',
  MOBILE = 'mobile',
}

export enum StreamType {
  LIVESTREAM = 'livestream',
  DEFAULT = 'default',
}

export enum CallType {
  VIDEO = 'videocall',
  AUDIO = 'call',
}

export enum CallMode {
  SINGLE = 'single',
  GROUP = 'group',
}

export enum StreamDestination {
  FEED = 'FEED',
  GROUPS = 'GROUPS',
  EVENTS = 'EVENTS',
}

export enum AudienceTypes {
  USERS = 'livestream_viewers',
  VIEWS = 'livestream_viewers_stats',
}

export enum AutoplayMode {
  AUTO = 'AUTO',
  ALWAYS = 'ALWAYS',
}

export type DestinationDetails = {
  id: number;
  icon?: Icon;
  avatar?: AvatarProps;
  title: string;
  description: string;
  supportDisableComments: boolean;
  hideAddTitleBox: boolean;
  hideViewPostButton: boolean;
  goBackText?: string;
};

export type DestinationActions = {
  commentPost: (
    postId: number,
    data: PostCommentPayload,
  ) => Promise<AxiosResponse<Comment>>;
  createPost: (
    data: PostData | Partial<EventPostType>,
  ) => Promise<AxiosResponse<GroupPost | Post>>;
  deletePost: (postId: number) => Promise<AxiosResponse>;
  getNextPage: (
    lastPage: AxiosResponse<GroupCommentList | Comment[]>,
    pages?: AxiosResponse<GroupCommentList | Comment[]>[],
    limit?: number,
  ) => number | string | undefined;
  getPostById: (postId: number) => Promise<AxiosResponse<GroupPost | Post>>;
  getPostComments: (
    postId: number,
    limit: number,
    page?: number | string,
  ) => Promise<AxiosResponse<GroupCommentList | Comment[]>>;
  goToDestination: () => void;
  invalidateQueries: () => void;
  toggleComments?: (
    postId: number,
    enable: boolean,
  ) => Promise<AxiosResponse<Post>>;
  transformPostComments: (
    pages?: AxiosResponse<GroupCommentList | Comment[]>[],
  ) => Comment[];
  unshiftComment: (postId: number, comment: Comment) => void;
  viewPost: (postId: number) => void;
  joinLiveStream: (
    postId: number,
  ) => Promise<AxiosResponse<JoinLivestreamResponse>>;
};

export type DestinationKeys = {
  commentsDetail: (postId: number) => Array<string | number>;
  postDetail: (postId: number) => Array<string | number>;
};

export type StreamDestinationInfo = {
  destination: StreamDestination;
  actions: DestinationActions;
  details?: DestinationDetails;
  keys: DestinationKeys;
  isLoading: boolean;
};

type InsightService<T> = {
  service: () => Promise<AxiosResponse<T>>;
  keys: Array<string | number>;
};

type PaginatedInsightService<T> = {
  service: ({
    q,
    limit,
    cursor,
    audienceType,
  }: {
    q: string;
    limit: number;
    cursor: string;
    audienceType: AudienceTypes;
  }) => Promise<AxiosResponse<CursorPagination<T>>>;
  keys: (type: AudienceTypes, query: string) => Array<string | number>;
};

export type LiveStreamInsights = {
  timeline: InsightService<LiveStreamTimeline>;
  stats: InsightService<LiveStreamStats>;
  viewerReports: PaginatedInsightService<LiveStreamViewerReport[]>;
  download: {
    onDownload: () => void;
    isDownloading: boolean;
  };
};

export enum FullScreenElementType {
  NONE = 'none',
  SEMI = 'semi',
  FULL = 'full',
}

export enum CallLayout {
  GRID = 'GRID_LAYOUT',
  SHARE_SCREEN = 'SHARE_SCREEN_LAYOUT',
}

// Livestream polls

export type Poll = {
  id: number;
  title?: string | null;
  totalAnswerCount: number;
  pollOptions: PollOption[];
  pollConfiguration: PollConfiguration;
  hasBeenAnswered: boolean;
};

export type PollOption = {
  id: number;
  option: string;
  position: number;
  answerCount: number;
  isLoggedUserAnswer: boolean;
  voters: Voter[];
};

export type PollConfiguration = {
  isAnonymous: boolean;
  endsAt: string | null;
  viewVotesResult: boolean;
  startsAt: string | null;
  showFinalResult: boolean;
};

export type Voter = {
  id: number;
  firstName: string;
  lastName: string;
  profilePicture: string | null;
};

export enum PollStatus {
  IDLE = 'IDLE',
  ACTIVE = 'ACTIVE',
  ENDED = 'ENDED',
}

export const PollFormSchema = z.object({
  title: z.string().min(1),
  pollOptions: z.array(z.object({ value: z.string().min(1) })),
  viewVotesResult: z.boolean(),
});

export type PollFormValues = z.infer<typeof PollFormSchema>;

export type PollResponse = {
  poll: Poll | null;
};

export type PollsResponse = {
  polls: Poll[];
};

export type LivestreamPollAnsweredEvent = {
  livestreamId: string;
  pollId: number;
  pollOptionId: number;
  userId: number;
};

export type PollUpdatedEvent = {
  livestreamId: string;
  pollId: number;
};
