import lodash from 'lodash';
import delay from 'delay';
import unixTimestamp from 'unix-timestamp';
import i18n from '@/plugins/i18n';
import { loggerContent } from '@/plugins/logger';
import { getCurrentUrl } from '@/lib/content';
import { shopeeUrlMatchProductId } from '@/lib/match';
import { imageUrl } from '@/adapter/shopee/render/index';
import { addCommas } from '@/lib/utils/str';
import { getShopeeProductId } from '@/api/shopee/client/product';
import {
  bridgeShopeeSaveProductId,
  bridgeGetUserInfo,
  bridgeOpenPopupUrl,
} from '@/bridge/content/background';
import { storageShopeeConfigProduct } from '@/plugins/storage/items';
import { getSettingApp } from '@content/service/get';
import { SHOPEE_SPY_ENABLED } from '@/constant/shopee';
import { ELEMENT_IDS, SELECTORS } from '@content/shopee/client/constant';

export class ProductDetailLoader {
  constructor() {
    this.elementProduct = {
      root: SELECTORS.PRODUCT_ROOT,
      trackedId: ELEMENT_IDS.PRODUCT_SPY_DETAIL,
      productReview: SELECTORS.PRODUCT_REVIEW,
      productDetail: SELECTORS.PRODUCT_DETAIL_HEADING,
      shopSelection: SELECTORS.SHOP_SELECTION,
      buttonIdSpy: ELEMENT_IDS.PRODUCT_SPY_BUTTON,
      buttonIdOpen: ELEMENT_IDS.PRODUCT_OPEN_BUTTON,
      buttonIdReview: ELEMENT_IDS.PRODUCT_REVIEW_BUTTON,
    };

    this.productId = '';
    this.shopId = '';

    /** @type {ShopeeProductData} */
    this.productData = {};
  }

  loadingText() {
    return `
      <div style='text-align:center'>
        <h1>${i18n.t('LOADING_DATA')} ...</h1>
      </div>
    `;
  }

  loadingError() {
    return `
      <div style='text-align:center'>
        <h1>${i18n.t('LOADING_ERROR')}</h1>
      </div>
    `;
  }

  createBtnBase(text, id) {
    const button = document.createElement('button');
    button.textContent = text;
    button.title = i18n.t('CREATED_BY_COPYRIGHT');
    button.id = id;
    button.style.cssText = `
      color: #ffffff;
      padding: 6px 12px;
      border: none;
      font-size: 13px;
      margin-left: 6px;
      border-radius: 3px;
      cursor: pointer;
      transition: all 0.2s ease;
      font-weight: 500;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
      height: 28px;
      line-height: 1;
    `;

    button.addEventListener('mouseover', () => {
      button.style.transform = 'translateY(-1px)';
      button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.15)';
    });

    button.addEventListener('mouseout', () => {
      button.style.transform = 'translateY(0)';
      button.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
    });

    return button;
  }

  /**
   * @param {object} rowData
   * @param {object} product
   * @param {boolean} isPaidPlan
   * @param {number} index
   * @returns {string}
   */
  renderRowTable(rowData, product, isPaidPlan, index = 0) {
    const {
      isHeader,
      name,
      price,
      price_before_discount,
      sold,
      stock,
      sale,
      image,
    } = rowData;

    const htmlImage = this.createImageCell(image, isHeader);
    const { showSold, showSale } = this.formatSoldAndSale(sold, price, product.currency, isPaidPlan);
    const rowStyle = this.getRowStyle(isHeader);
    const nameTdStyle = this.getNameTdStyle();

    return this.buildTableRow({
      rowStyle,
      index,
      name,
      nameTdStyle,
      htmlImage,
      price,
      price_before_discount,
      product,
      stock,
      isHeader,
      sold,
      sale,
      showSold,
      showSale,
    });
  }

  /**
   * @param {string} image
   * @param {boolean} isHeader
   * @returns {string}
   */
  createImageCell(image, isHeader) {
    const linkImage = imageUrl(image);

    if (isHeader) {
      return `<td>${image}</td>`;
    }

    if (linkImage) {
      return `<td>
        <a href="${linkImage}" target="_blank">
          <img src="${linkImage}" style="height: 60px; border-radius: 4px; object-fit: cover;" />
        </a>
      </td>`;
    }

    return `<td>${image}</td>`;
  }

  /**
   * @param {number} sold
   * @param {number} price
   * @param {string} currency
   * @param {boolean} isPaidPlan
   * @returns {object}
   */
  formatSoldAndSale(sold, price, currency, isPaidPlan) {
    const paidLabel = `<span style="color:#d0011b">${i18n.t('FEAUTRE_REQUIRE_PAID_PLAIN')}</span>`;

    const showSold = isPaidPlan
      ? (sold ? addCommas(sold) : i18n.t('AMOUNT_UNKNOWN'))
      : paidLabel;

    const showSale = isPaidPlan
      ? (sold ? addCommas(sold * price, currency) : i18n.t('AMOUNT_UNKNOWN'))
      : paidLabel;

    return { showSold, showSale };
  }

  /**
   * @param {boolean} isHeader
   * @returns {string}
   */
  getRowStyle(isHeader) {
    return isHeader ? 'background: #f8f9fa; font-weight: 600;' : '';
  }

  /**
   * @returns {string}
   */
  getNameTdStyle() {
    return 'padding: 12px; max-width: 80px; white-space: normal; word-break: break-word;';
  }

  /**
   * @param {object} params
   * @returns {string}
   */
  buildTableRow(params) {
    const {
      rowStyle,
      index,
      name,
      nameTdStyle,
      htmlImage,
      price,
      price_before_discount,
      product,
      stock,
      isHeader,
      sold,
      sale,
      showSold,
      showSale,
    } = params;

    return [
      `<tr style="${rowStyle}" class="table-row">`,
      `<td style="padding: 12px;">${index}</td>`,
      `<td style="${nameTdStyle}">${name}</td>`,
      htmlImage,
      `<td style="padding: 12px;">${isHeader ? price : addCommas(price, product.currency)}</td>`,
      `<td style="padding: 12px;">${isHeader ? price_before_discount : addCommas(price_before_discount || price, product.currency)}</td>`,
      `<td style="padding: 12px;">${stock || i18n.t('AMOUNT_UNKNOWN')}</td>`,
      `<td style="padding: 12px;">${isHeader ? sold : showSold}</td>`,
      `<td style="padding: 12px;">${isHeader ? sale : showSale}</td>`,
      '</tr>',
    ].join('');
  }

  /**
   * @param {string} itemId
   * @param {string} shopId
   * @param {boolean} isPaidPlan
   * @returns {Promise<boolean|null>}
   */
  async renderTableSpyProduct(itemId, shopId, isPaidPlan = false) {
    if (!this.canRenderTable()) {
      return false;
    }

    const elementAdd = this.createTableElement();
    elementAdd.innerHTML = this.loadingText();

    try {
      /** @type {ShopeeProductData} */
      const productData = await this.fetchProductData(itemId, shopId);

      if (productData.name) {
        this.productData = productData;
      }

      if (!this.isValidProduct(productData)) {
        elementAdd.innerHTML = this.loadingError();
        return null;
      }

      bridgeShopeeSaveProductId(productData);

      const tableDetail = this.createTableDetail(productData, isPaidPlan);
      const tableModel = this.createTableModel(productData, isPaidPlan);

      elementAdd.innerHTML = [tableDetail, tableModel].join('<div style="height:50px"></div>');
    } catch (error) {
      elementAdd.innerHTML = this.loadingError();
    }
  }

  /**
   * @returns {boolean}
   */
  canRenderTable() {
    return !!(document.querySelector(this.elementProduct.root) && !document.querySelector(`#${this.elementProduct.trackedId}`));
  }

  /**
   * @returns {HTMLElement}
   */
  createTableElement() {
    const elementAdd = document.createElement('div');
    elementAdd.id = this.elementProduct.trackedId;
    document.querySelector(this.elementProduct.root).prepend(elementAdd);
    return elementAdd;
  }

  /**
   * @param {string} itemId
   * @param {string} shopId
   * @returns {Promise<object>}
   */
  async fetchProductData(itemId, shopId) {
    return await getShopeeProductId(itemId, shopId, 'content');
  }

  /**
   * @param {object} product
   * @returns {boolean}
   */
  isValidProduct(product) {
    return !!(product && product.name);
  }

  /**
   * @param {object} product
   * @param {boolean} isPaidPlan
   * @returns {string}
   */
  createTableDetail(product, isPaidPlan) {
    const tableStyle = this.getTableStyle();
    const tablePayload = this.createTablePayload(product, isPaidPlan);
    const tableDetailBody = this.createTableDetailBody(tablePayload);

    return `
      ${tableStyle}
      <table class="tg">
        <tr>
          <td colspan="2" class="text-center" style="background: #f8f9fa;">
            <h3 style="margin: 0; padding: 10px;">${i18n.t('CREATED_BY_COPYRIGHT')}</h3>
          </td>
        </tr>
        ${tableDetailBody}
      </table>
    `;
  }

  /**
   * @returns {string}
   */
  getTableStyle() {
    return `
      <style>
        .tg {
          width: 100%;
          border-collapse: collapse;
          margin: 20px 0;
          font-size: 14px;
        }
        .tg td {
          border: 1px solid #e0e0e0;
          padding: 12px;
          vertical-align: middle;
          white-space: normal;
          word-break: break-word;
        }
        .tg tr:hover {
          background-color: #f5f5f5;
        }
        .text-center {
          text-align: center;
        }
        .text-bolder {
          font-weight: 600;
        }
        .tg-div {
          margin: 20px 0;
          border-radius: 8px;
          box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
      </style>
    `;
  }

  /**
   * @param {object} product
   * @param {boolean} isPaidPlan
   * @returns {Array}
   */
  createTablePayload(product, isPaidPlan) {
    return [
      {
        label: i18n.t('PRODUCT_NAME'),
        value: product.name,
      },
      {
        label: i18n.t('SHOP_LOCATION'),
        value: product.shop_data ? product.shop_data.place || product.shop_data.shop_location : '',
      },
      {
        label: i18n.t('SHOP_SOLD_TOTAL'),
        value: product.shop_data ? addCommas(product.shop_data.sold_total) : 0,
      },
      {
        label: i18n.t('SHOP_PRODUCT_TOTAL'),
        value: product.shop_data ? addCommas(product.shop_data.item_count) : 0,
      },
      {
        label: i18n.t('PRODUCT_TIME_CREATE'),
        value: unixTimestamp.toDate(product.ctime).toLocaleString(),
      },
      {
        label: i18n.t('PRODUCT_SALE_BY_ALLTIME'),
        value: this.formatHistoricalSale(product, isPaidPlan),
      },
      {
        label: [i18n.t('PRODUCT_STOCK'), i18n.t('PRODUCT_LIKE_COUNT'), i18n.t('PRODUCT_REVIEW_COUNT')].join(' / '),
        value: this.formatStockAndStats(product),
      },
      {
        label: i18n.t('PRODUCT_PRICE_MAX'),
        value: this.formatPriceRange(product.price_max, product.price_max_before_discount, product.currency),
      },
      {
        label: i18n.t('PRODUCT_PRICE_CURRENT'),
        value: this.formatPriceRange(product.price, product.price_before_discount, product.currency),
      },
      {
        label: i18n.t('PRODUCT_PRICE_MIN'),
        value: this.formatPriceRange(product.price_min, product.price_min_before_discount, product.currency),
      },
    ];
  }

  /**
   * @param {object} product
   * @param {boolean} isPaidPlan
   * @returns {string}
   */
  formatHistoricalSale(product, isPaidPlan) {
    if (isPaidPlan) {
      return [addCommas(product.historical_sold), addCommas(product.historical_sold * product.price, product.currency)].join(' / ');
    }
    return i18n.t('FEAUTRE_REQUIRE_PAID_PLAIN');
  }

  /**
   * @param {object} product
   * @returns {string}
   */
  formatStockAndStats(product) {
    return [addCommas(product.stock), addCommas(product.liked_count), addCommas(lodash.get(product, 'rating_count[0]', 0))].join(' / ');
  }

  /**
   * @param {number} price
   * @param {number} priceBeforeDiscount
   * @param {string} currency
   * @returns {string}
   */
  formatPriceRange(price, priceBeforeDiscount, currency) {
    return `${[addCommas(price, currency), addCommas(priceBeforeDiscount, currency)].join(' / ')} (${i18n.t('PRODUCT_BEFORE_AND_AFTER_DISCOUNT')})`;
  }

  /**
   * @param {Array} tablePayload
   * @returns {string}
   */
  createTableDetailBody(tablePayload) {
    return tablePayload.map(({ label, value }) => `<tr>${[`<td><b>${label}</b></td>`, `<td>${value}</td>`].join('')}</tr>`).join('');
  }

  /**
   * @param {object} product
   * @param {boolean} isPaidPlan
   * @returns {string}
   */
  createTableModel(product, isPaidPlan) {
    if (!product.models || !product.models.length) {
      return '';
    }

    const sortedModels = product.models.sort(({ sold: sold1 }, { sold: sold2 }) => sold2 - sold1);
    const tableModelBody = [
      {
        isHeader: true,
        index: '#',
        name: i18n.t('PRODUCT_MODEL_NAME'),
        image: i18n.t('PRODUCT_IMAGE_NAME'),
        price: i18n.t('PRODUCT_MODEL_PRICE'),
        price_before_discount: i18n.t('PRODUCT_MODEL_PRICE_BEFORE_DISCOUNT'),
        sold: i18n.t('PRODUCT_MODEL_SOLD'),
        stock: i18n.t('PRODUCT_MODEL_STOCK'),
        sale: i18n.t('PRODUCT_MODEL_TOTAL_SALE'),
      },
      ...sortedModels,
    ]
      .map((item, index) => this.renderRowTable(item, product, isPaidPlan, index))
      .join('');

    return `<table class="tg">${tableModelBody}</table>`;
  }

  /**
   * @returns {Promise<boolean>}
   */
  async initButtonProduct() {
    const { shopId, productId } = this.extractProductInfo();

    if (shopId && productId) {
      this.shopId = shopId;
      this.productId = productId;
    }

    if (!this.hasValidProductInfo(shopId, productId)) {
      return false;
    }

    if (document.querySelector(this.elementProduct.productDetail)) {
      await this.addMainButtons(shopId, productId);
    }

    await this.addReviewButton(shopId, productId);

    return true;
  }

  /**
   * @returns {object}
   */
  extractProductInfo() {
    const getUrl = getCurrentUrl();
    return shopeeUrlMatchProductId(getUrl);
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   * @returns {boolean}
   */
  hasValidProductInfo(shopId, productId) {
    return !!(shopId && productId);
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   * @returns {Promise<void>}
   */
  async addMainButtons(shopId, productId) {
    const { useBetaMode } = await getSettingApp();
    const { spyDetail } = await storageShopeeConfigProduct.getValue();
    const { isPaidPlan } = await bridgeGetUserInfo();

    if (!document.querySelector(`#${this.elementProduct.buttonIdSpy}`)) {
      await this.addSpyButtons(shopId, productId, spyDetail, isPaidPlan, useBetaMode);
    }

    if (!document.querySelector(`#${this.elementProduct.buttonIdOpen}`)) {
      this.addCloneButton(shopId, productId);
    }
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   * @param {boolean} spyDetail
   * @param {boolean} isPaidPlan
   * @param {boolean} useBetaMode
   * @returns {Promise<void>}
   */
  async addSpyButtons(shopId, productId, spyDetail, isPaidPlan, useBetaMode) {
    if (SHOPEE_SPY_ENABLED && spyDetail) {
      this.renderTableSpyProduct(productId, shopId, isPaidPlan);
    } else if (SHOPEE_SPY_ENABLED) {
      this.addSpyFastButton(productId, shopId, isPaidPlan);
    }

    if (SHOPEE_SPY_ENABLED && useBetaMode) {
      this.addSpyDetailButton(shopId, productId);
    }

    this.addSpyShopButton(shopId);
  }

  /**
   * @param {string} productId
   * @param {string} shopId
   * @param {boolean} isPaidPlan
   */
  addSpyFastButton(productId, shopId, isPaidPlan) {
    try {
      const buttonSpyFast = this.createBtnBase(i18n.t('BTN_PRODUCT_SPY_FAST'), this.elementProduct.buttonIdSpy);
      buttonSpyFast.style['margin-top'] = '5px';
      buttonSpyFast.style.background = '#282828';
      buttonSpyFast.addEventListener('click', () => this.renderTableSpyProduct(productId, shopId, isPaidPlan));
      document.querySelector(this.elementProduct.productDetail).append(buttonSpyFast);
    } catch (error) {
      loggerContent.error({
        msg: 'ADD_SPY_FAST_BUTTON_ERROR',
        error,
      });
    }
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   */
  addSpyDetailButton(shopId, productId) {
    try {
      const buttonSpyProduct = this.createBtnBase(i18n.t('BTN_PRODUCT_SPY_DETAIL'), this.elementProduct.buttonIdSpy);
      buttonSpyProduct.style['margin-top'] = '5px';
      buttonSpyProduct.style.background = 'rgb(0 143 39)';
      buttonSpyProduct.addEventListener('click', () => bridgeOpenPopupUrl({
        path: '/shopee/spy/product',
        qs: { shopId, productId },
      }));
      document.querySelector(this.elementProduct.productDetail).append(buttonSpyProduct);
    } catch (error) {
      loggerContent.error({
        msg: 'ADD_SPY_DETAIL_BUTTON_ERROR',
        error,
      });
    }
  }

  /**
   * @param {string} shopId
   */
  addSpyShopButton(shopId) {
    try {
      const shopSelectionElement = document.querySelector(this.elementProduct.shopSelection);

      if (!shopSelectionElement) {
        loggerContent.warn('Shop selection element not found');
        return;
      }

      const buttonSpyShop = this.createBtnBase(i18n.t('SPY_SHOP_BTN'), this.elementProduct.buttonIdSpy);
      buttonSpyShop.style['margin-top'] = '5px';
      buttonSpyShop.style.background = 'rgb(140 0 143)';
      buttonSpyShop.addEventListener('click', () => bridgeOpenPopupUrl({
        path: `/shopee/spy/shop/${shopId}`,
      }));
      shopSelectionElement.prepend(buttonSpyShop);
    } catch (error) {
      loggerContent.error({
        msg: 'ADD_SPY_SHOP_BUTTON_ERROR',
        error,
      });
    }
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   */
  addCloneButton(shopId, productId) {
    try {
      const buttonProductClone = this.createBtnBase(i18n.t('BTN_PRODUCT_CLONE'), this.elementProduct.buttonIdOpen);
      buttonProductClone.style.background = 'rgb(60 0 177)';
      buttonProductClone.addEventListener('click', () => bridgeOpenPopupUrl({
        path: '/shopee/product/clone',
        qs: { shopId, productId },
      }));
      document.querySelector(this.elementProduct.productDetail).append(buttonProductClone);
    } catch (error) {
      loggerContent.error({
        msg: 'ADD_CLONE_BUTTON_ERROR',
        error,
      });
    }
  }

  /**
   * @param {string} shopId
   * @param {string} productId
   * @returns {Promise<void>}
   */
  async addReviewButton(shopId, productId) {
    delay(2000).then(() => {
      if (document.querySelector(this.elementProduct.productReview) && !document.querySelector(`#${this.elementProduct.buttonIdReview}`)) {
        try {
          const buttonReview = this.createBtnBase(i18n.t('BTN_REVIEW_GET'), this.elementProduct.buttonIdReview);
          buttonReview.style.background = 'rgb(0 143 39)';
          buttonReview.style.height = '30px';
          buttonReview.addEventListener('click', () => bridgeOpenPopupUrl({
            path: '/shopee/product/review/get',
            qs: { shopId, productId },
          }));
          document.querySelector(this.elementProduct.productReview).appendChild(buttonReview);
        } catch (error) {
          loggerContent.error({
            msg: 'ADD_REVIEW_BUTTON_ERROR',
            error,
          });
        }
      }
    });
  }

  /**
   *
   * @param {ShopeeProductSelectVariationPayload} payloadSelect
   * @returns {Promise<boolean>}
   */
  async clickSelectVariant(payloadSelect = {}) {
    const {
      productId = '', shopId = '', listSelect = [],
    } = payloadSelect;

    if (!(Number(this.shopId) === Number(shopId) && Number(this.productId) === Number(productId))) {
      return false;
    }

    const delayClick = 500;

    const variantLists = [
      document.querySelectorAll('section.items-center:nth-child(1) > div > div.items-center > button'),
      document.querySelectorAll('section.items-center:nth-child(2) > div > div.items-center > button'),
      document.querySelectorAll('section.items-center:nth-child(3) > div > div.items-center > button'),
    ].filter((buttons) => buttons && buttons.length);

    /**
     *
     * @param {number[]} titer
     * @returns {Promise<boolean>}
     */
    const clickVariantByTiter = async (titer) => {
      if (!titer || !Array.isArray(titer) || titer.length === 0) {
        return false;
      }

      for (let level = 0; level < titer.length; level += 1) {
        const targetIndex = titer[level];
        const currentVariantList = variantLists[level];

        if (!currentVariantList || targetIndex < 0 || targetIndex >= currentVariantList.length) {
          return false;
        }

        const targetVariant = currentVariantList[targetIndex];

        if (
          !targetVariant
          || targetVariant.getAttribute('aria-disabled') === 'true'
        ) {
          return false;
        }

        targetVariant.click();
        await delay(delayClick);
      }

      return true;
    };

    await listSelect.reduce(async (previousPromise, item) => {
      await previousPromise;
      if (item.titer && Array.isArray(item.titer)) {
        await clickVariantByTiter(item.titer);
      }
    }, Promise.resolve());

    return true;
  }

  async initialize() {
    await this.initButtonProduct();
  }
}
