/* eslint-disable prefer-const */
type Role = "popup" | "background" | "content";
type Target = "popup" | "background" | "content";

interface IMessage<T = any> {
  from: Role;
  to: Target;
  type: string;
  payload?: T;
}

type MessageHandler<T = any> = (
  payload: T,
  sender: chrome.runtime.MessageSender,
  sendResponse: (response?: any) => void
) => void;

export default class MessageService {
  private role: Role;
  private listeners: Map<string, MessageHandler>;

  constructor(role: Role) {
    this.role = role;
    this.listeners = new Map();

    chrome.runtime.onMessage.addListener(
      (message: IMessage, sender, sendResponse) => {
        if (message?.type && this.listeners.has(message.type)) {
          const handler = this.listeners.get(message.type)!;
          handler(message.payload, sender, sendResponse);
          return true; // cho phép async response
        }
      }
    );
  }

  /**
   * Đăng ký handler cho một loại message
   */
  on<T = any>(type: string, handler: MessageHandler<T>): void {
    this.listeners.set(type, handler as MessageHandler);
  }

  /**
   * Gửi message tới target
   */
  send<T = any>(to: Target, type: string, payload?: T): void {
    const message: IMessage<T> = { from: this.role, to, type, payload };

    if (this.role === "popup" || this.role === "content") {
      // popup hoặc content luôn gửi qua background
      chrome.runtime.sendMessage(message);
    }

    if (this.role === "background") {
      if (to === "content") {
        chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
          if (tabs[0]?.id) {
            chrome.tabs.sendMessage(tabs[0].id, message, () => {
              if (chrome.runtime.lastError) {
                console.warn(
                  "Không gửi được tới content script:",
                  chrome.runtime.lastError.message
                );
              }
            });
          }
        });
      } else if (to === "popup") {
        chrome.runtime.sendMessage(message, () => {
          if (chrome.runtime.lastError) {
            console.warn(
              "Không gửi được tới popup:",
              chrome.runtime.lastError.message
            );
          }
        });
      }
    }
  }

  /**
   * Gửi message trực tiếp tới tab chỉ định (chỉ dùng cho background)
   */
  sendToTab<T = any>(tabId: number, type: string, payload?: T): void {
    if (this.role !== "background") {
      throw new Error("sendToTab chỉ được gọi từ background");
    }

    const message: IMessage<T> = {
      from: this.role,
      to: "content",
      type,
      payload,
    };

    chrome.tabs.sendMessage(tabId, message, () => {
      if (chrome.runtime.lastError) {
        console.warn(
          `Không gửi được tới content script ở tab ${tabId}:`,
          chrome.runtime.lastError.message
        );
      }
    });
  }

  waitForMessage<T = any>(type: string, timeout = 30000): Promise<T> {
    return new Promise((resolve, reject) => {
      let timer: any;

      const handler: MessageHandler<T> = (payload) => {
        clearTimeout(timer);
        this.off(type, handler);
        resolve(payload);
      };

      // timeout
      timer = setTimeout(() => {
        this.off(type, handler);
        reject(new Error(`Timeout khi chờ message: ${type}`));
      }, timeout);

      this.on(type, handler);
    });
  }

  off(type: string, handler: MessageHandler): void {
    const current = this.listeners.get(type);
    if (current === handler) {
      this.listeners.delete(type);
    }
  }
}
