import lodash from 'lodash';
import Promise from 'bluebird';
import { createIntegratedUi } from 'wxt/utils/content-script-ui/integrated';

import queryString from 'query-string';
import delay from 'delay';

import i18n from '@/plugins/i18n';
import { SHOPEE_DOMAIN_HOME } from '@/config/api';
import { shopeeUrlMatchProductId } from '@/lib/match';
import { renderProductDetail, videoUrl } from '@/adapter/shopee/render';
import { refactorProduct } from '@/adapter/shopee/product';
import { adapterDetailProduct } from '@/adapter/shopee/adapter';
import { PostMessageFetchUrl } from '@/bridge/content/postMessage';
import { bridgeGetUserInfo, bridgeOpenPopupUrl } from '@/bridge/content/background';
import { getBase64Url, zipFileBase64, saveAs } from '@/lib/utils/save';
import { loggerContent } from '@/plugins/logger';
import {
  ELEMENT_IDS,
  CSS_CLASSES,
  BULK_DOWNLOAD,
} from '@content/shopee/client/constant';

/**
 * @typedef {object} BulkProductCard
 * @property {HTMLElement} host
 * @property {string} productId
 * @property {string} shopId
 * @property {string} key
 */

const BULK_COLORS = {
  shopee: '#ee4d2d',
  video: '#2d8cf0',
  text: '#1f2329',
  sub: '#515a6e',
  border: '#eef0f2',
  hover: '#f5f6f7',
};

const BAR_STYLE = {
  position: 'fixed',
  left: '50%',
  bottom: '24px',
  transform: 'translateX(-50%) translateY(8px)',
  zIndex: '99999',
  display: 'none',
  alignItems: 'center',
  gap: '4px',
  padding: '8px 10px',
  background: '#ffffff',
  color: BULK_COLORS.text,
  borderRadius: '14px',
  border: `1px solid ${BULK_COLORS.border}`,
  boxShadow: '0 10px 34px rgba(17,24,39,0.18)',
  fontSize: '13px',
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
  fontWeight: '600',
  whiteSpace: 'nowrap',
  opacity: '0',
  transition: 'opacity 0.18s ease, transform 0.18s ease',
};

const BADGE_STYLE = {
  minWidth: '24px',
  height: '24px',
  padding: '0 7px',
  borderRadius: '999px',
  background: BULK_COLORS.shopee,
  color: '#fff',
  fontSize: '13px',
  fontWeight: '700',
  display: 'inline-flex',
  alignItems: 'center',
  justifyContent: 'center',
  lineHeight: '1',
};

const GHOST_BTN_STYLE = {
  padding: '7px 10px',
  background: 'transparent',
  color: BULK_COLORS.sub,
  border: '0',
  borderRadius: '8px',
  cursor: 'pointer',
  fontSize: '13px',
  fontWeight: '600',
  transition: 'background 0.15s ease',
};

const PRIMARY_BTN_STYLE = {
  display: 'inline-flex',
  alignItems: 'center',
  gap: '6px',
  padding: '8px 14px',
  color: '#fff',
  border: '0',
  borderRadius: '10px',
  cursor: 'pointer',
  fontSize: '13px',
  fontWeight: '700',
  transition: 'filter 0.15s ease, opacity 0.15s ease',
};

const DIVIDER_STYLE = {
  width: '1px',
  height: '22px',
  background: BULK_COLORS.border,
  margin: '0 4px',
};

const CHECKBOX_WRAP_STYLE = {
  position: 'absolute',
  top: '6px',
  right: '6px',
  zIndex: '30',
  width: '26px',
  height: '26px',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  background: 'rgba(255,255,255,0.9)',
  borderRadius: '4px',
  boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
  cursor: 'pointer',
};

export class BulkDownloadLoader {
  /**
   * @param {import('wxt/utils/content-script-context').ContentScriptContext} ctx
   */
  constructor(ctx) {
    this.ctx = ctx;

    /** @type {Map<string, {productId: string, shopId: string}>} */
    this.selection = new Map();

    this.ui = null;
    this.bar = null;
    this.badgeEl = null;
    this.statusEl = null;
    this.downloadImageBtn = null;
    this.downloadVideoBtn = null;
    this.busy = false;
    this.observer = null;

    /** @type {BridgeGetUserData} */
    this.userInfo = null;
  }

  async fetchUserInfo() {
    try {
      this.userInfo = await bridgeGetUserInfo();
    } catch (error) {
      this.userInfo = null;
    }

    return this.userInfo;
  }

  /**
   * @returns {Promise<boolean>}
   */
  async ensureLoggedIn() {
    const userInfo = await this.fetchUserInfo();

    if (userInfo && userInfo.isAuthed) {
      return true;
    }

    this.showToast(i18n.t('BULK_REQUIRE_LOGIN'));
    bridgeOpenPopupUrl({ path: '/login' }).catch(() => {});

    return false;
  }

  /**
   * @param {string} productId
   * @param {string} shopId
   * @returns {string}
   */
  itemKey(productId, shopId) {
    return `${shopId}_${productId}`;
  }

  /**
   * @returns {BulkProductCard[]}
   */
  getProductCards() {
    const anchors = [...document.querySelectorAll('a[href]')];
    const seen = new Set();

    /** @type {BulkProductCard[]} */
    const cards = [];

    anchors.forEach((anchor) => {
      const href = anchor.getAttribute('href') || '';
      const match = shopeeUrlMatchProductId(href);

      if (!(match && match.productId && match.shopId)) {
        return;
      }

      const key = this.itemKey(match.productId, match.shopId);

      if (seen.has(key)) {
        return;
      }

      const image = anchor.querySelector('img');

      if (!image) {
        return;
      }

      const host = image.closest('div') || image.parentElement;

      if (!host) {
        return;
      }

      seen.add(key);

      cards.push({
        host,
        productId: match.productId,
        shopId: match.shopId,
        key,
      });
    });

    return cards;
  }

  renderCheckboxes() {
    const cards = this.getProductCards();

    cards.forEach((card) => {
      this.addCheckbox(card);
    });
  }

  /**
   * @param {BulkProductCard} card
   */
  addCheckbox(card) {
    if (card.host.querySelector(`.${CSS_CLASSES.BULK_SELECT_WRAP}`)) {
      return;
    }

    const wrap = document.createElement('label');
    wrap.className = CSS_CLASSES.BULK_SELECT_WRAP;
    Object.assign(wrap.style, CHECKBOX_WRAP_STYLE);

    const checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.className = CSS_CLASSES.BULK_SELECT_CB;
    checkbox.style.width = '20px';
    checkbox.style.height = '20px';
    checkbox.style.cursor = 'pointer';
    checkbox.style.margin = '0';
    checkbox.style.accentColor = '#ee4d2d';
    checkbox.checked = this.selection.has(card.key);

    const blockNavigation = (event) => {
      event.stopPropagation();
    };

    wrap.addEventListener('click', blockNavigation);
    wrap.addEventListener('mousedown', blockNavigation);
    checkbox.addEventListener('click', blockNavigation);
    checkbox.addEventListener('mousedown', blockNavigation);

    checkbox.addEventListener('change', (event) => {
      event.stopPropagation();
      this.toggleSelect(card, checkbox.checked);
    });

    const position = window.getComputedStyle(card.host).position;

    if (position === 'static') {
      card.host.style.position = 'relative';
    }

    wrap.appendChild(checkbox);
    card.host.appendChild(wrap);

    this.applyCardHighlight(card.host, checkbox.checked);
  }

  /**
   * @param {BulkProductCard} card
   * @param {boolean} checked
   */
  toggleSelect(card, checked) {
    if (checked) {
      this.selection.set(card.key, {
        productId: card.productId,
        shopId: card.shopId,
      });
    } else {
      this.selection.delete(card.key);
    }

    this.applyCardHighlight(card.host, checked);
    this.refreshBar();
  }

  /**
   * @param {HTMLElement} host
   * @param {boolean} checked
   */
  applyCardHighlight(host, checked) {
    if (checked) {
      host.style.outline = '3px solid #ee4d2d';
      host.style.outlineOffset = '-3px';
      host.style.borderRadius = '8px';
    } else {
      host.style.outline = '';
      host.style.outlineOffset = '';
    }
  }

  selectAllVisible() {
    const cards = this.getProductCards();

    cards.forEach((card) => {
      this.selection.set(card.key, {
        productId: card.productId,
        shopId: card.shopId,
      });

      const checkbox = card.host.querySelector(`.${CSS_CLASSES.BULK_SELECT_CB}`);

      if (checkbox) {
        checkbox.checked = true;
      }

      this.applyCardHighlight(card.host, true);
    });

    this.refreshBar();
  }

  clearSelection() {
    this.selection.clear();

    document.querySelectorAll(`.${CSS_CLASSES.BULK_SELECT_CB}`).forEach((checkbox) => {
      checkbox.checked = false;
      const host = checkbox.closest(`.${CSS_CLASSES.BULK_SELECT_WRAP}`)?.parentElement;

      if (host) {
        this.applyCardHighlight(host, false);
      }
    });

    this.refreshBar();
  }

  mountBar() {
    if (this.ui) {
      return;
    }

    this.ui = createIntegratedUi(this.ctx, {
      position: 'overlay',
      zIndex: 99999,
      onMount: (container) => {
        const bar = document.createElement('div');
        bar.id = ELEMENT_IDS.BULK_ACTION_BAR;
        Object.assign(bar.style, BAR_STYLE);

        const badge = document.createElement('span');
        Object.assign(badge.style, BADGE_STYLE);
        badge.textContent = '0';

        const status = document.createElement('span');
        status.style.cssText = `color:${BULK_COLORS.sub};margin:0 6px 0 8px;font-weight:600;`;
        status.textContent = i18n.t('BULK_SELECTED_LABEL');

        const selectAllBtn = this.createGhostButton(i18n.t('BULK_SELECT_ALL'), () => {
          this.selectAllVisible();
        });

        const clearBtn = this.createGhostButton(i18n.t('BULK_CLEAR'), () => {
          this.clearSelection();
        });

        const imageBtn = this.createPrimaryButton(i18n.t('BULK_DOWNLOAD_IMAGE'), '🖼', BULK_COLORS.shopee, () => {
          this.bulkDownloadImages().catch(() => {});
        });

        const videoBtn = this.createPrimaryButton(i18n.t('BULK_DOWNLOAD_VIDEO'), '🎬', BULK_COLORS.video, () => {
          this.bulkDownloadVideos().catch(() => {});
        });

        const closeBtn = this.createCloseButton(() => {
          this.clearSelection();
        });

        bar.appendChild(badge);
        bar.appendChild(status);
        bar.appendChild(this.createDivider());
        bar.appendChild(selectAllBtn);
        bar.appendChild(clearBtn);
        bar.appendChild(this.createDivider());
        bar.appendChild(imageBtn);
        bar.appendChild(videoBtn);
        bar.appendChild(closeBtn);

        container.appendChild(bar);

        this.bar = bar;
        this.badgeEl = badge;
        this.statusEl = status;
        this.downloadImageBtn = imageBtn;
        this.downloadVideoBtn = videoBtn;

        this.refreshBar();
      },
    });

    this.ui.mount();
  }

  /**
   * @returns {HTMLElement}
   */
  createDivider() {
    const divider = document.createElement('span');
    Object.assign(divider.style, DIVIDER_STYLE);

    return divider;
  }

  /**
   * @param {string} label
   * @param {Function} onClick
   * @returns {HTMLElement}
   */
  createGhostButton(label, onClick) {
    const button = document.createElement('button');
    button.type = 'button';
    button.textContent = label;
    Object.assign(button.style, GHOST_BTN_STYLE);

    button.addEventListener('mouseenter', () => {
      button.style.background = BULK_COLORS.hover;
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
    });

    button.addEventListener('click', onClick);

    return button;
  }

  /**
   * @param {string} label
   * @param {string} icon
   * @param {string} color
   * @param {Function} onClick
   * @returns {HTMLElement}
   */
  createPrimaryButton(label, icon, color, onClick) {
    const button = document.createElement('button');
    button.type = 'button';
    Object.assign(button.style, PRIMARY_BTN_STYLE);
    button.style.background = color;

    const iconSpan = document.createElement('span');
    iconSpan.textContent = icon;
    iconSpan.style.fontSize = '14px';

    const labelSpan = document.createElement('span');
    labelSpan.textContent = label;

    button.appendChild(iconSpan);
    button.appendChild(labelSpan);

    button.addEventListener('mouseenter', () => {
      button.style.filter = 'brightness(0.92)';
    });

    button.addEventListener('mouseleave', () => {
      button.style.filter = 'none';
    });

    button.addEventListener('click', onClick);

    return button;
  }

  /**
   * @param {Function} onClick
   * @returns {HTMLElement}
   */
  createCloseButton(onClick) {
    const button = document.createElement('button');
    button.type = 'button';
    button.textContent = '✕';

    button.style.cssText = `
      margin-left:2px;
      width:26px;
      height:26px;
      border:0;
      border-radius:8px;
      background:transparent;
      color:#b0b4bb;
      font-size:14px;
      cursor:pointer;
      transition:background 0.15s ease, color 0.15s ease;
    `;

    button.addEventListener('mouseenter', () => {
      button.style.background = BULK_COLORS.hover;
      button.style.color = BULK_COLORS.sub;
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
      button.style.color = '#b0b4bb';
    });

    button.addEventListener('click', onClick);

    return button;
  }

  refreshBar() {
    if (!this.bar) {
      return;
    }

    const total = this.selection.size;

    if (this.badgeEl) {
      this.badgeEl.textContent = String(total);
    }

    if (this.statusEl) {
      this.statusEl.textContent = i18n.t('BULK_SELECTED_LABEL');
    }

    if (total > 0) {
      this.bar.style.display = 'flex';
      this.ctx.setTimeout(() => {
        this.bar.style.opacity = '1';
        this.bar.style.transform = 'translateX(-50%) translateY(0)';
      }, 10);
    } else {
      this.bar.style.opacity = '0';
      this.bar.style.transform = 'translateX(-50%) translateY(8px)';
      this.bar.style.display = 'none';
    }
  }

  /**
   * @param {string} text
   */
  setBarText(text) {
    if (this.statusEl) {
      this.statusEl.textContent = text;
    }
  }

  /**
   * @param {string} message
   */
  showToast(message) {
    const toast = document.createElement('div');
    toast.textContent = message;

    Object.assign(toast.style, {
      position: 'fixed',
      left: '50%',
      bottom: '72px',
      transform: 'translateX(-50%)',
      zIndex: '100000',
      padding: '10px 16px',
      background: '#1f2329',
      color: '#fff',
      borderRadius: '8px',
      fontSize: '13px',
      fontWeight: '600',
      boxShadow: '0 6px 20px rgba(0,0,0,0.25)',
      maxWidth: '80vw',
      textAlign: 'center',
    });

    document.body.appendChild(toast);

    this.ctx.setTimeout(() => {
      toast.remove();
    }, 3000);
  }

  /**
   * @param {boolean} disabled
   */
  setBusy(disabled) {
    this.busy = disabled;

    [this.downloadImageBtn, this.downloadVideoBtn].forEach((button) => {
      if (!button) {
        return;
      }

      button.disabled = disabled;
      button.style.opacity = disabled ? '0.5' : '1';
      button.style.pointerEvents = disabled ? 'none' : 'auto';
    });
  }

  /**
   * @param {string} name
   * @param {string} productId
   * @returns {string}
   */
  safeFolderName(name, productId) {
    const base = lodash.snakeCase((name || '').slice(0, 40)) || 'product';

    return `${base}_${productId}`;
  }

  /**
   * @param {string} productId
   * @param {string} shopId
   * @returns {Promise<ShopeeProductData|null>}
   */
  async resolveProductDetail(productId, shopId) {
    try {
      const url = queryString.stringifyUrl({
        url: `${SHOPEE_DOMAIN_HOME}/api/v4/pdp/get_pc`,
        query: {
          shop_id: shopId,
          item_id: productId,
        },
      });

      const raw = await PostMessageFetchUrl.sendMessage({ url, method: 'GET' });

      if (!(raw && raw.data)) {
        return null;
      }

      const detail = await adapterDetailProduct(raw.data);

      return refactorProduct(detail);
    } catch (error) {
      loggerContent.error({
        msg: 'BULK_RESOLVE_PRODUCT_ERROR',
        productId,
        shopId,
        error,
      });

      return null;
    }
  }

  /**
   * @param {ShopeeProductData} product
   * @returns {string[]}
   */
  resolveVideoUrls(product) {
    const list = lodash.get(product, 'video_info_list', []) || [];
    const urls = [];

    list.forEach((video) => {
      if (video.formats && video.formats[0] && video.formats[0].url) {
        urls.push(video.formats[0].url);
        return;
      }

      if (video.default_format && video.default_format.url) {
        urls.push(video.default_format.url);
        return;
      }

      if (video.video_id) {
        urls.push(videoUrl(video.video_id));
      }
    });

    if (!urls.length && product.video) {
      urls.push(product.video);
    }

    return urls;
  }

  checkSoftLimit() {
    if (this.selection.size > BULK_DOWNLOAD.softLimit) {
      this.showToast(i18n.t('BULK_DOWNLOAD_LIMIT_WARN', { max: BULK_DOWNLOAD.softLimit }));
    }
  }

  async bulkDownloadImages() {
    if (this.busy) {
      return;
    }

    if (!this.selection.size) {
      this.showToast(i18n.t('BULK_DOWNLOAD_EMPTY'));
      return;
    }

    if (!(await this.ensureLoggedIn())) {
      return;
    }

    this.checkSoftLimit();
    this.setBusy(true);

    const items = [...this.selection.values()];
    const listFile = [];
    let done = 0;
    let ok = 0;
    let failed = 0;

    await Promise.mapSeries(items, async (item, index) => {
      if (index > 0) {
        await delay(BULK_DOWNLOAD.fetchDelayMs);
      }

      try {
        const product = await this.resolveProductDetail(item.productId, item.shopId);

        if (!product) {
          failed++;
          return;
        }

        const detail = renderProductDetail(product);
        const folder = this.safeFolderName(product.name, item.productId);

        await Promise.map(detail.listImage, async (image, indexImage) => {
          try {
            const base64 = await getBase64Url(image.url);

            if (base64) {
              listFile.push({
                fileName: `${folder}/${indexImage + 1}_${image.fileName}`,
                base64,
              });
            }
          } catch (error) {
            loggerContent.error({
              msg: 'BULK_IMAGE_BASE64_ERROR',
              url: image.url,
              error,
            });
          }
        }, { concurrency: BULK_DOWNLOAD.imageConcurrency });

        ok++;
      } catch (error) {
        failed++;
        loggerContent.error({
          msg: 'BULK_DOWNLOAD_IMAGE_ERROR',
          item,
          error,
        });
      } finally {
        done++;
        this.setBarText(i18n.t('BULK_DOWNLOAD_PROGRESS', { done, total: items.length }));
      }
    });

    if (!listFile.length) {
      this.showToast(i18n.t('BULK_DOWNLOAD_NO_IMAGE'));
      this.setBusy(false);
      this.refreshBar();
      return;
    }

    this.setBarText(i18n.t('BULK_DOWNLOAD_ZIPPING'));

    const zipName = `shopee-images-${Date.now()}.zip`;

    await zipFileBase64(listFile, zipName).catch((error) => {
      loggerContent.error({
        msg: 'BULK_DOWNLOAD_ZIP_ERROR',
        error,
      });
    });

    this.showToast(i18n.t('BULK_DOWNLOAD_IMAGE_DONE', { ok, failed }));

    this.setBusy(false);
    this.refreshBar();
  }

  async bulkDownloadVideos() {
    if (this.busy) {
      return;
    }

    if (!this.selection.size) {
      this.showToast(i18n.t('BULK_DOWNLOAD_EMPTY'));
      return;
    }

    if (!(await this.ensureLoggedIn())) {
      return;
    }

    this.checkSoftLimit();
    this.setBusy(true);

    const items = [...this.selection.values()];
    let done = 0;
    let ok = 0;
    let skipped = 0;

    await Promise.mapSeries(items, async (item, index) => {
      if (index > 0) {
        await delay(BULK_DOWNLOAD.fetchDelayMs);
      }

      try {
        const product = await this.resolveProductDetail(item.productId, item.shopId);

        if (!product) {
          skipped++;
          return;
        }

        const urls = this.resolveVideoUrls(product);

        if (!urls.length) {
          skipped++;
          return;
        }

        const folder = this.safeFolderName(product.name, item.productId);

        await Promise.mapSeries(urls, async (url, index) => {
          try {
            await saveAs(url, `${folder}_${index + 1}.mp4`);
          } catch (error) {
            window.open(url, '_blank');
          }
        });

        ok++;
      } catch (error) {
        skipped++;
        loggerContent.error({
          msg: 'BULK_DOWNLOAD_VIDEO_ERROR',
          item,
          error,
        });
      } finally {
        done++;
        this.setBarText(i18n.t('BULK_DOWNLOAD_PROGRESS', { done, total: items.length }));
      }
    });

    this.showToast(i18n.t('BULK_DOWNLOAD_VIDEO_DONE', { ok, skipped }));

    this.setBusy(false);
    this.refreshBar();
  }

  observe() {
    const root = document.body;

    if (!root || this.observer) {
      return;
    }

    this.observer = new MutationObserver(lodash.debounce(() => {
      this.renderCheckboxes();
    }, 400));

    this.observer.observe(root, {
      childList: true,
      subtree: true,
    });

    this.ctx.onInvalidated(() => {
      if (this.observer) {
        this.observer.disconnect();
        this.observer = null;
      }
    });
  }

  initialize() {
    try {
      this.mountBar();
      this.renderCheckboxes();
      this.observe();
      this.fetchUserInfo().catch(() => {});
    } catch (error) {
      loggerContent.error({
        msg: 'BULK_INIT_ERROR',
        error,
      });
    }
  }
}
