import { log }          from '@juzi/wechaty-puppet'

import {
  ContactImpl,
  ContactSelfImpl,
  DelayImpl,
  FriendshipImpl,
  ImageImpl,
  LocationImpl,
  MessageImpl,
  MiniProgramImpl,
  PostImpl,
  RoomImpl,
  RoomInvitationImpl,
  TagImpl,
  TagGroupImpl,
  UrlLinkImpl,
  ChannelImpl,
  MomentImpl,
  CallRecordImpl,
  ChatHistoryImpl,
  WecomImpl,

  ContactConstructor,
  ContactSelfConstructor,
  DelayConstructor,
  FriendshipConstructor,
  ImageConstructor,
  LocationConstructor,
  MessageConstructor,
  MiniProgramConstructor,
  PostConstructor,
  RoomConstructor,
  RoomInvitationConstructor,
  TagConstructor,
  TagGroupConstructor,
  UrlLinkConstructor,
  ChannelConstructor,
  MomentConstructor,
  CallRecordConstructor,
  ChatHistoryConstructor,
  WecomConstructor,

  wechatifyUserModule,
}                       from '../user-modules/mod.js'

import type {
  WechatySkeleton,
}                             from '../wechaty/mod.js'

const wechatifyUserModuleMixin = <MixinBase extends typeof WechatySkeleton> (mixinBase: MixinBase) => {
  log.verbose('WechatifyUserModuleMixin', 'wechatifyUserModuleMixin(%s)', mixinBase.name)

  abstract class WechatifyUserModuleMixin extends mixinBase {

    constructor (...args: any[]) {
      log.verbose('WechatifyUserModuleMixin', 'constructor()')
      super(...args)
    }

    __wechatifiedContact?        : ContactConstructor
    __wechatifiedContactSelf?    : ContactSelfConstructor
    __wechatifiedDelay?          : DelayConstructor
    __wechatifiedFriendship?     : FriendshipConstructor
    __wechatifiedImage?          : ImageConstructor
    __wechatifiedLocation?       : LocationConstructor
    __wechatifiedMessage?        : MessageConstructor
    __wechatifiedMiniProgram?    : MiniProgramConstructor
    __wechatifiedPost?           : PostConstructor
    __wechatifiedRoom?           : RoomConstructor
    __wechatifiedRoomInvitation? : RoomInvitationConstructor
    __wechatifiedTag?            : TagConstructor
    __wechatifiedTagGroup?       : TagGroupConstructor
    __wechatifiedUrlLink?        : UrlLinkConstructor
    __wechatifiedChannel?        : ChannelConstructor
    __wechatifiedMoment?         : MomentConstructor
    __wechatifiedCallRecord?     : CallRecordConstructor
    __wechatifiedChatHistory?    : ChatHistoryConstructor
    __wechatifiedWecom?          : WecomConstructor

    get Contact ()        : ContactConstructor        { return guardWechatify(this.__wechatifiedContact)        }
    get ContactSelf ()    : ContactSelfConstructor    { return guardWechatify(this.__wechatifiedContactSelf)    }
    get Delay ()          : DelayConstructor          { return guardWechatify(this.__wechatifiedDelay)          }
    get Friendship ()     : FriendshipConstructor     { return guardWechatify(this.__wechatifiedFriendship)     }
    get Image ()          : ImageConstructor          { return guardWechatify(this.__wechatifiedImage)          }
    get Location ()       : LocationConstructor       { return guardWechatify(this.__wechatifiedLocation)       }
    get Message ()        : MessageConstructor        { return guardWechatify(this.__wechatifiedMessage)        }
    get MiniProgram ()    : MiniProgramConstructor    { return guardWechatify(this.__wechatifiedMiniProgram)    }
    get Post ()           : PostConstructor           { return guardWechatify(this.__wechatifiedPost)           }
    get Room ()           : RoomConstructor           { return guardWechatify(this.__wechatifiedRoom)           }
    get RoomInvitation () : RoomInvitationConstructor { return guardWechatify(this.__wechatifiedRoomInvitation) }
    get Tag ()            : TagConstructor            { return guardWechatify(this.__wechatifiedTag)            }
    get TagGroup ()       : TagGroupConstructor       { return guardWechatify(this.__wechatifiedTagGroup)       }
    get UrlLink ()        : UrlLinkConstructor        { return guardWechatify(this.__wechatifiedUrlLink)        }
    get Channel ()        : ChannelConstructor        { return guardWechatify(this.__wechatifiedChannel)        }
    get Moment ()         : MomentConstructor         { return guardWechatify(this.__wechatifiedMoment)         }
    get CallRecord ()     : CallRecordConstructor     { return guardWechatify(this.__wechatifiedCallRecord)     }
    get ChatHistory ()    : ChatHistoryConstructor    { return guardWechatify(this.__wechatifiedChatHistory)    }
    get Wecom ()          : WecomConstructor          { return guardWechatify(this.__wechatifiedWecom)          }

    override async init (): Promise<void> {
      log.verbose('WechatifyUserModuleMixin', 'init()')
      await super.init()

      /**
       * Skip if already wechatified
       */
      if (this.__wechatifiedMessage) {
        log.verbose('WechatifyUserModuleMixin', 'init() Wechaty User Module (WUM)s have already wechatified: skip')
        return
      }

      log.verbose('WechatifyUserModuleMixin', 'init() initializing Wechaty User Module (WUM) ...')

      /**
       * Wechatify User Classes
       *  1. Binding the wechaty instance to the class
       *
       * Huan(202110): FIXME: remove any
       */
      this.__wechatifiedContact        = wechatifyUserModule(ContactImpl)(this as any)
      this.__wechatifiedContactSelf    = wechatifyUserModule(ContactSelfImpl)(this as any)
      this.__wechatifiedDelay          = wechatifyUserModule(DelayImpl)(this as any)
      this.__wechatifiedFriendship     = wechatifyUserModule(FriendshipImpl)(this as any)
      this.__wechatifiedImage          = wechatifyUserModule(ImageImpl)(this as any)
      this.__wechatifiedLocation       = wechatifyUserModule(LocationImpl)(this as any)
      this.__wechatifiedMessage        = wechatifyUserModule(MessageImpl)(this as any)
      this.__wechatifiedMiniProgram    = wechatifyUserModule(MiniProgramImpl)(this as any)
      this.__wechatifiedPost           = wechatifyUserModule(PostImpl)(this as any)
      this.__wechatifiedRoom           = wechatifyUserModule(RoomImpl)(this as any)
      this.__wechatifiedRoomInvitation = wechatifyUserModule(RoomInvitationImpl)(this as any)
      this.__wechatifiedTag            = wechatifyUserModule(TagImpl)(this as any)
      this.__wechatifiedTagGroup       = wechatifyUserModule(TagGroupImpl)(this as any)
      this.__wechatifiedUrlLink        = wechatifyUserModule(UrlLinkImpl)(this as any)
      this.__wechatifiedChannel        = wechatifyUserModule(ChannelImpl)(this as any)
      this.__wechatifiedMoment         = wechatifyUserModule(MomentImpl)(this as any)
      this.__wechatifiedCallRecord     = wechatifyUserModule(CallRecordImpl)(this as any)
      this.__wechatifiedChatHistory    = wechatifyUserModule(ChatHistoryImpl)(this as any)
      this.__wechatifiedWecom          = wechatifyUserModule(WecomImpl)(this as any)

      log.verbose('WechatifyUserModuleMixin', 'init() initializing Wechaty User Module (WUM) ... done')
    }

  }

  return WechatifyUserModuleMixin
}

/**
 * Huan(202008): we will bind the wechaty puppet with user modules (Contact, Room, etc) together inside the start() method
 */
function guardWechatify<T extends Function> (userModule?: T): T {
  if (userModule) {
    return userModule
  }
  throw new Error('Wechaty User Module (WUM, for example: wechaty.Room) can not be used before wechaty.start()!')
}

type WechatifyUserModuleMixin = ReturnType<typeof wechatifyUserModuleMixin>

type ProtectedPropertyWechatifyUserModuleMixin =
  | '__wechatifiedContact'
  | '__wechatifiedContactSelf'
  | '__wechatifiedDelay'
  | '__wechatifiedFriendship'
  | '__wechatifiedImage'
  | '__wechatifiedLocation'
  | '__wechatifiedMessage'
  | '__wechatifiedMiniProgram'
  | '__wechatifiedRoom'
  | '__wechatifiedRoomInvitation'
  | '__wechatifiedTag'
  | '__wechatifiedTagGroup'
  | '__wechatifiedUrlLink'
  | '__wechatifiedChannel'
  | '__wechatifiedMoment'
  | '__wechatifiedCallRecord'
  | '__wechatifiedChatHistory'
  | '__wechatifiedWecom'

export type {
  WechatifyUserModuleMixin,
  ProtectedPropertyWechatifyUserModuleMixin,
}
export {
  wechatifyUserModuleMixin,
}
