import { CLIENT_ID } from 'mweb/common/config/twitchClientID';

const TMI = require('twitch-tmi.js');

const DEBUG_TMI_CLIENT = false;

export interface UserStatePayloadBadges {
  readonly [badgeset: string]: string;
}

export interface UserStatePayloadEmotes {
  readonly [emote: string]: string[];
}

export interface UserStatePayload {
  readonly badges: UserStatePayloadBadges | null;
  readonly bits: number | undefined;
  readonly color: string | null;
  readonly 'display-name': string;
  readonly emotes: UserStatePayloadEmotes | null;
  readonly id: string;
  readonly 'user-id': string;
  readonly username: string;
}

export interface SubscriptionMethods {
  prime: boolean;
}

export interface ConnectedHandler {
  (address: string, port: number): void;
}
export interface ConnectingHandler {
  (address: string, port: number): void;
}
export interface DisconnectedHandler {
  (reason: string): void;
}
export interface ReconnectHandler {
  (): void;
}

export interface HostingHandler {
  (channel: string, target: string, viewers: number): void;
}
export interface UnhostHandler {
  (channel: string, viewers: number): void;
}

export interface ChatHandler {
  (
    channel: string,
    userstate: UserStatePayload,
    message: string,
    sentByCurrentUser: boolean,
  ): void;
}

export interface ModerationHandler {
  (channel: string, username: string, reason: string, duration?: number): void;
}

export interface SubscriptionHandler {
  (channel: string, username: string, methods: SubscriptionMethods): void;
}

export interface ResubscriptionHandler {
  (
    channel: string,
    username: string,
    months: number,
    message: string,
    userstate: UserStatePayload,
    methods: SubscriptionMethods,
  ): void;
}

type TMIEventHandler =
  | ConnectedHandler
  | DisconnectedHandler
  | ReconnectHandler
  | HostingHandler
  | UnhostHandler
  | ChatHandler
  | ModerationHandler
  | SubscriptionHandler
  | ResubscriptionHandler;

type SocketState = 'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED';

interface TMIClient {
  connect: () => Promise<void>;
  disconnect: () => Promise<void>;
  join: (channel: string) => Promise<void>;
  part: (channel: string) => Promise<void>;
  addListener: (event: string, handler: TMIEventHandler) => void;
  readyState: () => SocketState;
}

export class ChatClient {
  client: TMIClient;
  currentChannel: string;

  constructor() {
    this.client = TMI.client({
      connection: {
        reconnect: true,
        secure: true,
        timeout: 2000,
      },
      options: {
        debug: DEBUG_TMI_CLIENT,
        clientId: CLIENT_ID,
      },
    });
  }

  async connect(channel: string): Promise<void> {
    this.currentChannel = channel;
    await this.client.connect();
    await this.client.join(this.currentChannel);
  }

  disconnect(): Promise<void> {
    return this.client.disconnect();
  }

  async changeChannel(channel: string): Promise<void> {
    if (this.currentChannel) {
      await this.client.part(this.currentChannel);
    }
    this.currentChannel = channel;
    await this.client.join(this.currentChannel);
  }

  setConnectedHandler(connectedHandler: ConnectedHandler): void {
    this.client.addListener('connected', connectedHandler);
  }

  setDisconnectedHandler(disconnectedHandler: DisconnectedHandler): void {
    this.client.addListener('disconnected', disconnectedHandler);
  }

  setReconnectHandler(reconnectHandler: ReconnectHandler): void {
    this.client.addListener('reconnect', reconnectHandler);
  }

  setHostingHandler(hostingHandler: HostingHandler): void {
    this.client.addListener('hosting', hostingHandler);
  }

  setUnhostHandler(unhostHandler: UnhostHandler): void {
    this.client.addListener('unhost', unhostHandler);
  }

  setChatHandler(chatHandler: ChatHandler): void {
    this.client.addListener('chat', chatHandler);
    this.client.addListener('cheer', chatHandler);
  }

  setActionHandler(actionHandler: ChatHandler): void {
    this.client.addListener('action', actionHandler);
  }

  setTimeoutHandler(timeoutHandler: ModerationHandler): void {
    this.client.addListener('timeout', timeoutHandler);
  }

  setBanHandler(banHandler: ModerationHandler): void {
    this.client.addListener('ban', banHandler);
  }

  setSubcriptionHandler(subcriptionHandler: SubscriptionHandler): void {
    this.client.addListener('subscription', subcriptionHandler);
  }

  setResubscriptionHandler(resubscriptionHandler: ResubscriptionHandler): void {
    this.client.addListener('resub', resubscriptionHandler);
  }

  isSocketOpen(): boolean {
    return this.client.readyState() === 'OPEN';
  }
}

const chatClient = new ChatClient();
export default chatClient;
