import axios from 'axios';
import { workerConnect, workerEvent } from '@/socket/worker';
import { handlderJobFetch, getFetchJobId, handlerJobFetchWorkerId } from '@/api/worker/fetch';
import { loggerContent } from '@/plugins/logger';
import { configApp } from '@/config/app';
import { SHOPEE_DOMAIN_HOME } from '@/config/api';
import {
  storageUserId,
  storageUserName,
  storageBrowserIp,
  storageBrowserName,
  storageProfileId,
  storageAppUuid,
} from '@/plugins/storage/items';
import { checkCrawlerDetected } from '@/api/shopee/getter';
import { PostMessageFetchUrl } from '@/bridge/content/postMessage';

const retrySetUpWorker = 60000;

const shopeeApi = axios.create({
  baseURL: SHOPEE_DOMAIN_HOME,
});

shopeeApi.interceptors.response.use((response) => response.data);

class WorkerFetchUrlService {
  constructor() {
    this.workerData = {
      isConnected: true,
      isActive: true,
      isReady: false,
      lastestLog: '',
      name: '',
      id: '',
      fetchJob: {
        count: 0,
        id: '',
        lastest: null,
      },
    };
    this.socketWorker = null;
    this.socketId = null;
  }

  logMessage(msg, data) {
    loggerContent.log({ msg, ...data });
  }

  logError(msg, data) {
    loggerContent.error({ msg, ...data });
  }

  async preCheckWorker() {
    return true;
  }

  async fetchUrlWorker(payload) {
    const { workerData } = this;

    if (!(workerData.isConnected && workerData.isActive)) {
      return false;
    }

    if (!workerData.isReady) {
      this.logError('WORKER_READY_DOWN', { workerData });
      throw new Error('FETCH_URL_WORKER_SET_DOWN');
    }

    const { jobId, url: fetchUrl, post: postData } = payload;
    const isFetchPost = !!postData;

    this.logMessage('FETCH_URL_START', { workerData, jobId, fetchUrl });

    const isActive = await this.preCheckWorker();

    if (!isActive) {
      this.setWorkerDown();
      workerData.isReady = false;
      throw new Error('FETCH_URL_WORKER_SET_DEACTIVE');
    }

    this.logMessage('FETCH_URL_SENDING', { workerName: workerData.name, fetchUrl });

    if (jobId) {
      await getFetchJobId(jobId, isFetchPost);
    }

    const payloadSend = {
      url: fetchUrl,
      method: isFetchPost ? 'POST' : 'GET',
      body: postData || null,
    };

    const fetchValue = await PostMessageFetchUrl.sendMessage(payloadSend);

    this.logMessage('FETCH_URL_REQUEST', { workerName: workerData.name, fetchUrl });

    if (checkCrawlerDetected(fetchValue)) {
      this.logMessage('FETCH_URL_ERROR', {
        workerName: workerData.name,
        fetchUrl,
        fetchValue,
        workerData,
      });
      this.setWorkerDown('CRAWLER_DETECTED');
      setTimeout(() => this.setWorkerUp(), retrySetUpWorker);
      throw new Error('FETCH_URL_CRAWLER_DETECTED');
    }

    this.logMessage('FETCH_URL_FINISH', {
      workerName: workerData.name,
      fetchUrl,
      fetchValue,
      workerData,
    });

    workerData.fetchJob.count++;
    workerData.fetchJob.lastest = new Date();

    if (jobId) {
      await handlderJobFetch(jobId, fetchValue, `background:${workerData.name}`, isFetchPost).catch((error) => {
        this.logError('FETCH_SAVE_JOB_ERROR', { error });
      });
    }

    return fetchValue;
  }

  async setWorkerIndentify() {
    const { workerData, socketWorker } = this;

    const [username, userId, browserIp, browserName, profileId, appId] = await Promise.all([
      storageUserName.getValue(),
      storageUserId.getValue(),
      storageBrowserIp.getValue(),
      storageBrowserName.getValue(),
      storageProfileId.getValue(),
      storageAppUuid.getValue(),
    ]);

    const payloadEmit = {
      from: 'content-script',
      workerName: workerData.name,
      workerId: workerData.id,
      version: configApp.verText,
      manifest: configApp.manifest,
      username,
      userId,
      browserIp,
      browserName,
      profileId,
      appId,
    };

    this.logMessage('SET_WORKER_INDENTIFY', payloadEmit);

    socketWorker.emit(workerEvent.workerSetIndentify, payloadEmit);
  }

  setWorkerUp(log = '') {
    const { workerData, socketWorker } = this;
    this.logMessage('SET_WORKER_UP', { workerName: workerData.name });
    workerData.lastestLog = log;
    workerData.isReady = true;
    const payloadWorkerEmit = {
      workerName: workerData.name,
      workerId: workerData.id,
    };
    socketWorker.emit(workerEvent.workerReadyUp, payloadWorkerEmit);
  }

  setWorkerDown(log = '') {
    const { workerData, socketWorker } = this;
    this.logMessage('SET_WORKER_DOWN', { workerName: workerData.name });
    workerData.lastestLog = log;
    workerData.isReady = false;
    const payloadWorkerEmit = {
      workerName: workerData.name,
      workerId: workerData.id,
    };
    socketWorker.emit(workerEvent.workerReadyDown, payloadWorkerEmit);
  }

  getWorkerState() {
    const { workerData } = this;
    return workerData.isReady && workerData.isActive && workerData.isConnected ? 'WORKER UP' : 'WORKER DOWN';
  }

  async startService() {
    /** @type {ShopeeShopProfile} */
    const shopProfile = (await shopeeApi.get('/api/v2/user/profile/get/')).data;

    this.workerData.isReady = !!(shopProfile && shopProfile.username && shopProfile.phone);

    if (!this.workerData.isReady) {
      this.logError('WORKER_NO_READY_LOGIN');
      return false;
    }

    this.logMessage('WORKER_READY_CONNECT');

    this.workerData.name = shopProfile.username;
    this.workerData.id = shopProfile.shopid;

    this.socketWorker = workerConnect();
    this.socketId = this.socketWorker.id;

    if (false) {
      window._fetchWorkerApi = {
        fetchUrl: (payload) => this.fetchUrlWorker(payload),
        getSocketId: () => this.socketWorker.id,
        getSocketClient: () => this.socketWorker,
        getWorkerState: () => this.getWorkerState(),
        getWorkerData: () => this.workerData,
        setWorkerUp: () => this.setWorkerUp(),
        setWorkerDown: () => this.setWorkerDown(),
        setWorkerIndentify: () => this.setWorkerIndentify(),
      };
    }

    this.socketWorker.on(workerEvent.fetchUrlJobRecived, (payloadFetch) => {
      this.fetchUrlWorker(payloadFetch)
        .then((fetchDone) => this.logMessage('FETCH_URL_DONE', { fetchDone }))
        .catch((fetchError) => this.logMessage('FETCH_URL_ERROR', { fetchError }));
    });

    this.socketWorker.on(workerEvent.fetchUrlWorkerId, (payloadFetch) => {
      const { jobId } = payloadFetch;
      this.fetchUrlWorker({ ...payloadFetch, jobId: '' })
        .then((jobValue) => handlerJobFetchWorkerId(jobId, jobValue))
        .catch((error) => handlerJobFetchWorkerId(jobId, error));
    });

    this.socketWorker.on(workerEvent.connect, async () => {
      this.socketId = this.socketWorker.id;
      this.workerData.isConnected = true;
      this.workerData.isActive = true;
      this.setWorkerUp();
      this.setWorkerIndentify();
      this.logMessage('SOCKET_WORKER_CONNECT', { id: this.socketId });
    });

    this.socketWorker.on(workerEvent.disconnected, () => {
      this.workerData.isConnected = false;
      this.workerData.isActive = false;
      this.logError('SOCKET_WORKER_DISCONNECT', { id: this.socketId });
    });

    return this.socketWorker;
  }
}

const workerFetchService = async () => {
  const FetchApi = new WorkerFetchUrlService();

  return FetchApi.startService();
};

export default workerFetchService;
