

import { NIMAudioFile, NIMError, NIMFile, NIMGeo, NIMImageFile, NIMVideoFile } from './Basic';
import { NIMClientType } from './LoginPort';
import { IsInvalid, IsUndefinedLiteral, OmitStrict, Overwrite, PartialByKeys, RequiredByKeys, StringLike, StringWrap } from '@22g/utility-types';



/**
 * 消息场景类型
 * - [相关文档](https://dev.yunxin.163.com/docs/interface/即时通讯Web端/NIMSDK-Web/IMMessage.html#.scene)
 */
export const enum NIMSceneType {
  /** 单人聊天 */
  p2p = 'p2p',
  /** 群聊 */
  team = 'team',
  /** 超大群聊天 */
  superTeam = 'superTeam',
}




/** 消息发送状态 */
export const enum NIMMessageStatus {
  /** 发送中 */
  sending = 'sending',
  /** 发送成功 */
  success = 'success',
  /** 发送失败 */
  fail = 'fail',
}



/** 消息流向 */
export const enum NIMMessageFlow {
  /** 表示此消息是收到的消息 */
  in = 'in',
  /** 表示此消息是发出的消息 */
  out = 'out',
}




/**
 * 消息类型
 * - [相关文档](https://dev.yunxin.163.com/docs/interface/即时通讯Web端/NIMSDK-Web/IMMessage.html#.type)
 */
export const enum NIMMessageType {
  /** 文本 */
  text = 'text',
  /** 图片 */
  image = 'image',
  /** 音频 */
  audio = 'audio',
  /** `视频` */
  video = 'video',
  /** `文件` */
  file = 'file',
  /** `地理位置` */
  geo = 'geo',
  /** `自定义消息` */
  custom = 'custom',
  /** `提醒消息` */
  tip = 'tip',
  /**
   * `机器人消息`
   * - 提醒消息用于会话内的状态提醒，如进入会话时出现的欢迎消息，或者会话命中敏感词后的提示消息等等.
   */
  robot = 'robot',
  /**
   * `群通知消息`
   * - 某些群操作后所有群成员会收到一条相应的群通知消息, 详细介绍请参考群通知消息的类型[[NIMMessageAttach|`NIMMessageAttach`]]
   * - 此类消息不会计入未读数
   */
  notification = 'notification',
}


/**
 * 群通知消息
 * - [相关文档](https://dev.yunxin.163.com/docs/interface/即时通讯Web端/NIMSDK-Web/IMMessage.html#.attach)
 * - 群通知消息是消息的一种, 请参考消息类型, 某些群操作后所有群成员会收到一条相应的群通知消息
 * - 群通知消息有一个字段attach包含了额外的信息, attach有一个字段type来标识群通知消息的类型
 */
export const enum NIMMessageAttach {
  /**
   * 更新群
   * - 更新群后, 所有群成员会收到一条类型为'updateTeam'的群通知消息。此类群通知消息的from字段的值为更新群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为被更新的群信息。
   */
  updateTeam = 'updateTeam',
  /**
   * 拉人入群
   * - 普通群, 拉人入群后, 所有群成员会收到一条类型为'addTeamMembers'的群通知消息。此类群通知消息的from字段的值为拉人的人的帐号,to字段的值为对应的群ID,attach有一个字段team的值为对应的[群对象](#群对象),attach有一个字段accounts的值为被拉的人的帐号列表,attach有一个字段members`的值为被拉的群成员列表。
   * - 被邀请的群成员在有人说话后才能看到该群, 而且会先收到一条类型为'addTeamMembers'的群通知消息, 然后会收到其它群消息。
   */
  addTeamMembers = 'addTeamMembers',
  /**
   * 踢人出群
   * - 踢人出群后, 所有群成员会收到一条类型为'removeTeamMembers'的群通知消息。此类群通知消息的from字段的值为踢人的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段accounts的值为被踢的人的帐号列表。
   */
  removeTeamMembers = 'removeTeamMembers',
  /**
   * 接受入群邀请
   * - 高级群的群主和管理员在邀请成员加入群（通过操作创建群或拉人入群）之后, 被邀请的人会收到一条类型为'teamInvite'的系统通知, 此类系统通知的from字段的值为邀请方的帐号, to字段的值为对应的群ID, 此类系统通知的attach有一个字段team的值为被邀请进入的群, 被邀请的人可以选择接受邀请或者拒绝邀请。
   * - 如果接受入群邀请, 那么该群的所有群成员会收到一条类型为'acceptTeamInvite'的群通知消息, 此类群通知消息的from字段的值为接受入群邀请的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段members的值为接收入群邀请的群成员列表。
   * - 如果拒绝入群邀请, 那么邀请你的人会收到一条类型为'rejectTeamInvite'的系统通知, 此类系统通知的from字段的值为拒绝入群邀请的人的帐号, to字段的值为对应的群ID。
   */
  acceptTeamInvite = 'acceptTeamInvite',
  /**
   * 通过入群申请
   * - 用户可以主动申请加入高级群, 目标群的群主和管理员会收到一条类型为'applyTeam'的系统通知, 此类系统通知的from字段的值为申请方的帐号, to字段的值为对应的群ID, 高级群的群主和管理员在收到入群申请后, 可以选择通过或者拒绝入群申请。
   * - 如果通过入群申请, 那么该群的所有群成员会收到一条类型为'passTeamApply'的群通知消息, 此类群通知消息的from字段的值为通过入群申请的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段account包含了申请方的帐号, attach有一个字段members的值为被通过申请的群成员列表。
   * - 如果拒绝入群申请, 那么申请人会收到一条类型为'rejectTeamApply'的系统通知, 此类系统通知的from字段的值为拒绝方的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群。
   */
  passTeamApply = 'passTeamApply',
  /**
   * 添加群管理员
   * - 添加群管理员后, 所有群成员会收到一条类型为'addTeamManagers'的群通知消息。此类群通知消息的from字段的值为添加群管理员的人的帐号, to字段的值为对应的群ID, attach有一个字段accounts的值为被加为管理员的帐号列表, attach有一个字段members的值为被加为管理员的群成员列表。
   */
  addTeamManagers = 'addTeamManagers',
  /**
   * 移除群管理员
   * - 移除群管理员后, 所有群成员会收到一条类型为'removeTeamManagers'的群通知消息。此类群通知消息的from字段的值为移除群管理员的人的帐号, to字段的值为对应的群ID, attach有一个字段accounts的值为被移除的管理员的帐号列表, attach有一个字段members的值为被移除管理员的群成员列表。
   */
  removeTeamManagers = 'removeTeamManagers',
  /**
   * 主动退群
   * - 主动退群后, 所有群成员会收到一条类型为'leaveTeam'的群通知消息。此类群通知消息的from字段的值为退群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象。
   */
  leaveTeam = 'leaveTeam',
  /**
   * 解散群
   * - 解散群后, 所有群成员会收到一条类型为'dismissTeam'的群通知消息。此类群通知消息的from字段为解散群的人的帐号, to字段的值为被对应的群ID。
   */
  dismissTeam = 'dismissTeam',
  /**
   * 转让群
   * - 转让群后, 所有群成员会收到一条类型为'transferTeam'的群通知消息。此类群通知消息的from字段的值为转让群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段account的值为为新群主的帐号, attach有一个字段members的值为包含新旧群主的群成员列表。
   */
  transferTeam = 'transferTeam',
  /**
   * 更新群成员禁言状态
   * - 更新群成员禁言状态后, 所有群成员会收到一条类型为'updateTeamMute'的群通知消息。此类群通知消息的from字段的值为操作方, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段account的值为被禁言的帐号, attach有一个字段members的值为被禁言的群成员列表。
   */
  updateTeamMute = 'updateTeamMute',
  /**
   * 更新超大群
   * - 更新超大群后, 所有超大群群成员会收到一条类型为'updateSuperTeam'的群通知消息。此类群通知消息的from字段的值为更新群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为被更新的超大群信息。
   */
  updateSuperTeam = 'updateSuperTeam',
  /**
   * addSuperTeamMembers
   * - 被邀请的群成员在有人说话后才能看到该超大群, 而且会先收到一条类型为'addSuperTeamMembers'的群通知消息, 然后会收到其它群消息。
   */
  addSuperTeamMembers = 'addSuperTeamMembers',
  /**
   * 踢人出超大群
   * - 踢人出超大群后, 所有超大群群成员会收到一条类型为'removeSuperTeamMembers'的群通知消息。此类群通知消息的from字段的值为踢人的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段accounts的值为被踢的人的帐号列表。
   */
  removeSuperTeamMembers = 'removeSuperTeamMembers',
  /**
   * 主动退出超大群
   * - 主动退群后, 所有超大群群成员会收到一条类型为'leaveSuperTeam'的群通知消息。此类群通知消息的from字段的值为退群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象。
   */
  leaveSuperTeam = 'leaveSuperTeam',
  /**
   * 解散超大群
   * - 解散超大群后, 所有超大群群成员会收到一条类型为'dismissSuperTeam'的群通知消息。此类群通知消息的from字段为解散群的人的帐号, to字段的值为被对应的群ID。
   */
  dismissSuperTeam = 'dismissSuperTeam',
  /**
   * 转让超大群
   * - 调用服务端API转让群后, 所有群成员会收到一条类型为'transferSuperTeam'的群通知消息。此类群通知消息的from字段的值为转让群的人的帐号, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段account的值为为新群主的帐号, attach有一个字段members的值为包含新旧群主的群成员列表。
   */
  transferSuperTeam = 'transferSuperTeam',
  /**
   * 添加超大群管理员
   * - 添加群管理员后, 所有群成员会收到一条类型为'addSuperTeamManagers'的群通知消息。此类群通知消息的from字段的值为添加群管理员的人的帐号, to字段的值为对应的群ID, attach有一个字段accounts的值为被加为管理员的帐号列表, attach有一个字段members的值为被加为管理员的群成员列表。
   */
  addSuperTeamManagers = 'addSuperTeamManagers',
  /**
   * 移除超大群管理员
   * - 移除群管理员后, 所有群成员会收到一条类型为'removeSuperTeamManagers'的群通知消息。此类群通知消息的from字段的值为移除群管理员的人的帐号, to字段的值为对应的群ID, attach有一个字段accounts的值为被移除的管理员的帐号列表, attach有一个字段members的值为被移除管理员的群成员列表。
   */
  removeSuperTeamManagers = 'removeSuperTeamManagers',
  /**
   * 更新超大群成员禁言状态
   * - 更新群成员禁言状态后, 所有群成员会收到一条类型为'updateSuperTeamMembersMute'的群通知消息。此类群通知消息的from字段的值为操作方, to字段的值为对应的群ID, attach有一个字段team的值为对应的群对象, attach有一个字段account的值为被禁言的帐号, attach有一个字段members的值为被禁言的群成员列表。
   */
  updateSuperTeamMembersMute = 'updateSuperTeamMembersMute',
}

/** thread消息额外的字段 */
export interface NIMThreadMessageExProps {
  /** 被回复消息的发送者账号 */
  replyMsgFromAccount: string;
  /** 被回复消息的接受者账号 */
  replyMsgToAccount: string;
  /** 被回复消息的时间 */
  replyMsgTime: number;
  /** 被回复消息的idServer */
  replyMsgIdServer: string;
  /** 被回复消息的idClient */
  replyMsgIdClient: string;
  /** thread消息的发送者账号 */
  threadMsgFromAccount: string;
  /** thread消息的接受者账号 */
  threadMsgToAccount: string;
  /** thread消息的时间 */
  threadMsgTime: number;
  /** thread消息的idServer */
  threadMsgIdServer: string;
  /** thread消息的idClient */
  threadMsgIdClient: string;
  /** 表该消息是否已被撤回或单向删除，获取thread消息列表时会用到 */
  delete?: boolean;
}

export interface NIMBasicMessage extends Partial<NIMThreadMessageExProps> {
  /** 场景类型 */
  scene: NIMSceneType;
  /** 发送方账号 */
  from: string;
  /** 发送方的昵称 */
  fromNick: string;
  /** 发送方的设备类型 */
  fromClientType: NIMClientType;
  /** 发送端设备id */
  fromDeviceId: string;
  /** 消息接收方, 帐号或群id */
  to: string;
  /** 时间戳(毫秒) */
  time: number;
  /** 发送方信息更新时间  时间戳(毫秒) */
  userUpdateTime: number;
  /** 消息类型 */
  type: NIMMessageType;
  /** 消息所属的会话的ID */
  sessionId: string;
  /** 聊天对象, 账号或者群id */
  target: string;
  /**
   * 消息的流向
   * - `in` 表示此消息是收到的消息
   * - `out` 表示此消息是发出的消息
   */
  flow: NIMMessageFlow;
  /** 消息发送状态 */
  status: NIMMessageStatus;
  /**
   * 群通知消息
   */
  attach?: NIMMessageAttach;
  /** SDK生成的消息id, 在发送消息之后会返回给开发者, 开发者可以在发送消息的结果回调里面根据这个ID来判断相应消息的发送状态, 到底是发送成功了还是发送失败了, 然后根据此状态来更新页面的UI。如果发送失败, 那么可以重新发送此消息 */
  idClient: string;
  /** 服务器用于区分消息用的ID, 用于获取历史消息和获取包含关键词的历史消息, 此字段可能没有, 所以开发者应该使用idClient来唯一标识消息 */
  idServer?: string;
  /** 该消息在接收方是否应该被静音 */
  isMuted: boolean;
  /** 是否是重发的消息 */
  resend: boolean;
  /**
   * 扩展字段
   * - 推荐使用JSON格式构建, 非JSON格式的话, Web端会正常接收, 但是会被其它端丢弃
   */
  custom?: string;
  /** 自定义推送文案 */
  pushContent?: string;
  /**
   * 自定义的推送属性
   * - 推荐使用JSON格式构建, 非JSON格式的话, Web端会正常接收, 但是会被其它端丢弃
   */
  pushPayload?: string;
  /** 特殊推送选项, 只在群会话中使用 */
  apns?: unknown;
  /**
   * 本地自定义扩展字段
   * - 在支持数据库时可以调用更新本地消息来更新此字段, 此字段只会被更新到本地数据库, 不会被更新到服务器上
   */
  localCustom?: string;
  /** 是否存储云端历史 */
  isHistoryable: boolean;
  /** 是否支持漫游 */
  isRoamingable: boolean;
  /** 是否支持发送者多端同步 */
  isSyncable: boolean;
  /** 是否支持抄送 */
  cc: boolean;
  /** 是否需要推送 */
  isPushable: boolean;
  /** 是否要存离线 */
  isOfflinable: boolean;
  /** 是否计入消息未读数 */
  isUnreadable: boolean;
  /** 是否为应答消息（用于机器人等类似场景等应答消息内容） */
  isReplyMsg: boolean;
  /** 群已读消息快照大小（即消息发送时的群人数-1） */
  tempTeamMemberCount: number;
  /** 是否需要推送昵称 */
  needPushNick: boolean;
  /** 是否是本地消息, 请查阅发送文本消息 */
  isLocal: boolean;
}




/** IM 消息对象 */
export interface NIMMessage extends NIMBasicMessage {
  /** 文本消息的文本内容 */
  text?: string;
  /** 文件消息的文件对象 */
  file?: NIMFile | NIMAudioFile | NIMImageFile | NIMVideoFile;
  /** 地理位置消息的地理位置对象, */
  geo?: NIMGeo;
  /** 提醒消息的内容 */
  tip?: string;
  /** 自定义消息的消息内容, 开发者可以自行扩展, 建议封装成JSON格式字符串 */
  content?: string;
}

/** IM Thread 消息对象 */
export interface NIMThreadMessage extends Omit<NIMMessage, keyof NIMThreadMessageExProps>, NIMThreadMessageExProps {

}


type SpecialKeys = 'text' | 'file' | 'geo' | 'tip' | 'content';

type PureMessage<K extends SpecialKeys> = RequiredByKeys<Omit<NIMMessage, Exclude<SpecialKeys, K>>, Exclude<SpecialKeys, Exclude<SpecialKeys, K>>>;



/** `提醒`消息 */
export interface NIMTipMessage extends PureMessage<'tip'> {
  type: NIMMessageType.tip;

}

/**
 * `自定义`消息
 * @typeparam T 自定义内容
 */
export interface NIMCustomMessage<T = string> extends Omit<PureMessage<'content'>, 'content'> {
  type: NIMMessageType.custom;
  /** 自定义内容 */
  content: T;
}

/** `文本`消息 */
export interface NIMTextMessage extends PureMessage<'text'> {
  type: NIMMessageType.text;
}


/** `文件`消息 */
export interface NIMFileMessage<T extends NIMFile = NIMFile> extends PureMessage<'file'> {
  type: NIMMessageType.file;
  file: T;
}

/** `图片文件`消息 */
export interface NIMImageFileMessage extends PureMessage<'file'> {
  type: NIMMessageType.image;
  file: NIMImageFile;
}

/** `视频文件`消息 */
export interface NIMVideoFileMessage extends PureMessage<'file'> {
  type: NIMMessageType.video;
  file: NIMVideoFile;
}

/** `音频文件`消息 */
export interface NIMAudioFileMessage extends PureMessage<'file'> {
  type: NIMMessageType.audio;
  file: NIMAudioFile;
}


/** `地理位置`消息 */
export interface NIMGeoMessage extends PureMessage<'geo'> {
  type: NIMMessageType.geo;
}

/**
 * 发送消息时通用的参数
 * - [[NIM.sendCustomMsg|`NIM.sendCustomMsg`]]
 * - [[NIM.sendText|`NIM.sendText`]]
 * - [[NIM.sendFile|`NIM.sendFile`]]
 * - [[NIM.sendGeo|`NIM.sendGeo`]]
 * - [[NIM.sendTipMsg|`NIM.sendTipMsg`]]
 * - [[NIM.sendRobotMsg|`NIM.sendRobotMsg`]]
 */
export interface NIMSendMsgCommon<T extends NIMMessage = NIMMessage> extends Pick<T, 'scene' | 'to'>, Partial<Pick<T, 'custom' | 'pushContent' | 'pushPayload' | 'apns' | 'cc'>> {
  /**
   * 是否重发
   */
  resend?: T['resend'];
  /**
   * 如果是重发, 那么需要带上之前生成的 [[NIMMessage.idClient|`NIMMessage.idClient`]] 来标记这条消息
   */
  idClient?: T['idClient'];
  /**
   * 是否存储云端历史
   * @default true
   */
  isHistoryable?: T['isHistoryable'];
  /**
   * 是否支持漫游
   * @default true
   */
  isRoamingable?: T['isRoamingable'];
  /**
   * 是否支持发送者多端同步
   * @default true
   */
  isSyncable?: T['isSyncable'];
  /**
   * 是否需要推送
   * @default true
   */
  isPushable?: T['isPushable'];
  /**
   * 是否要存离线
   * @default true
   */
  isOfflinable?: T['isOfflinable'];
  /**
   * 是否计入消息未读数
   * @default true
   */
  isUnreadable?: T['isUnreadable'];
  /**
   * 是否需要推送昵称
   * @default true
   */
  needPushNick?: T['needPushNick'];
  /**
   * 是否是本地消息
   * - true表示本地消息, 那么SDK并不会发送此条消息, 而是直接调用回调表示发送成功, 并更新对应的会话
   * @default false
   */
  isLocal?: T['isLocal'];
  /**
   * 本地消息的发送方，默认是当前用户
   * - 只有发送本地消息时，才可自定义发送方
   * - true表示本地消息, 那么SDK并不会发送此条消息, 而是直接调用回调表示发送成功, 并更新对应的会话
   */
  localFrom?: T['from'];
}


export type NIMLocalMessageWrapper<T extends NIMBasicMessage> = Overwrite<T, {
  /** 本地消息 */
  isLocal: true;
}>;

