/**
 * 智能选择器 - 支持多策略元素查找
 * TypeScript版本，适配Puppeteer
 */

import { Page, ElementHandle } from 'puppeteer';

interface TextOptions {
  exact?: boolean;
  caseInsensitive?: boolean;
  selector?: string;
  filterDisabled?: boolean;
  filterHidden?: boolean;
}

interface Strategy {
  type: string;
  find: () => Promise<ElementHandle | null>;
}

export class SmartSelector {
  private page: Page;
  private strategies: Strategy[] = [];

  constructor(page: Page) {
    this.page = page;
  }

  /**
   * 从配置构建选择器
   */
  static fromConfig(config: any, page: Page): SmartSelector {
    const selector = new SmartSelector(page);

    if (typeof config === 'string') {
      // 简单 CSS 选择器
      selector.css(config);
    } else if (Array.isArray(config)) {
      // 多策略
      config.forEach((strategy: any) => {
        if (strategy.css) selector.css(strategy.css);
        if (strategy.xpath) selector.xpath(strategy.xpath);
        if (strategy.text) {
          const textOptions: TextOptions = {
            exact: strategy.exact,
            caseInsensitive: strategy.caseInsensitive,
            selector: strategy.selector,
            filterDisabled: strategy.filterDisabled,
            filterHidden: strategy.filterHidden
          };
          selector.text(strategy.text, textOptions);
        }
        if (strategy.placeholder) selector.placeholder(strategy.placeholder);
        if (strategy.label) selector.label(strategy.label);
        if (strategy.type) selector.type(strategy.type);
        if (strategy.role) selector.role(strategy.role);
        if (strategy.testid) selector.testid(strategy.testid);
        if (strategy.name) selector.name(strategy.name);
      });
    } else if (typeof config === 'object') {
      // 单个策略对象
      if (config.css) selector.css(config.css);
      if (config.xpath) selector.xpath(config.xpath);
      if (config.text) {
        const textOptions: TextOptions = {
          exact: config.exact,
          caseInsensitive: config.caseInsensitive,
          selector: config.selector,
          filterDisabled: config.filterDisabled,
          filterHidden: config.filterHidden
        };
        selector.text(config.text, textOptions);
      }
      if (config.placeholder) selector.placeholder(config.placeholder);
      if (config.label) selector.label(config.label);
      if (config.type) selector.type(config.type);
      if (config.role) selector.role(config.role);
      if (config.testid) selector.testid(config.testid);
      if (config.name) selector.name(config.name);
    }

    return selector;
  }

  css(selector: string): this {
    this.strategies.push({
      type: 'css',
      find: async () => await this.page.$(selector)
    });
    return this;
  }

  xpath(xpath: string): this {
    this.strategies.push({
      type: 'xpath',
      find: async () => {
        const elements = await this.page.$x(xpath);
        return elements[0] || null;
      }
    });
    return this;
  }

  /**
   * 文本选择器
   */
  text(text: string, options: TextOptions = {}): this {
    const {
      exact = true,
      caseInsensitive = true,
      selector = '*',
      filterDisabled = false,
      filterHidden = true
    } = options;

    this.strategies.push({
      type: 'text',
      find: async () => {
        return await this._findByText(text, selector, { exact, caseInsensitive, filterDisabled, filterHidden });
      }
    });
    return this;
  }

  /**
   * 内部方法：按文本查找元素
   */
  private async _findByText(
    searchText: string,
    cssSelector: string,
    options: Required<Omit<TextOptions, 'selector'>>
  ): Promise<ElementHandle | null> {
    const { exact, caseInsensitive, filterDisabled, filterHidden } = options;

    const element = await this.page.evaluateHandle(
      ({ searchText, exact, caseInsensitive, cssSelector, filterDisabled, filterHidden }) => {
        const elements = Array.from(document.querySelectorAll(cssSelector));

        for (const el of elements) {
          // 过滤 disabled
          if (filterDisabled && (el.tagName === 'BUTTON' || el.tagName === 'INPUT') && (el as any).disabled) {
            continue;
          }

          // 过滤隐藏元素
          if (filterHidden && (el as any).offsetParent === null) continue;

          // 获取文本
          const text = (el.textContent || '').trim();
          if (!text) continue;

          // 标准化空格
          const normalizedText = text.replace(/\s+/g, ' ');
          const normalizedSearch = searchText.replace(/\s+/g, ' ');

          // 匹配
          let matches = false;
          if (exact) {
            matches = caseInsensitive
              ? normalizedText.toLowerCase() === normalizedSearch.toLowerCase()
              : normalizedText === normalizedSearch;
          } else {
            matches = caseInsensitive
              ? normalizedText.toLowerCase().includes(normalizedSearch.toLowerCase())
              : normalizedText.includes(normalizedSearch);
          }

          if (matches) return el;
        }

        return null;
      },
      { searchText, exact, caseInsensitive, cssSelector, filterDisabled, filterHidden }
    );

    if (element) {
      const elementHandle = element.asElement();
      if (elementHandle) return elementHandle;
    }

    return null;
  }

  placeholder(placeholder: string): this {
    this.strategies.push({
      type: 'placeholder',
      find: async () => await this.page.$(`[placeholder="${placeholder}"]`)
    });
    return this;
  }

  label(labelText: string): this {
    this.strategies.push({
      type: 'label',
      find: async () => {
        const handle = await this.page.evaluateHandle((text: string) => {
          const labels = Array.from(document.querySelectorAll('label'));
          const label = labels.find(l => l.textContent?.trim() === text.trim());
          if (label && (label as HTMLLabelElement).htmlFor) {
            return document.getElementById((label as HTMLLabelElement).htmlFor);
          }
          return null;
        }, labelText);
        return handle.asElement();
      }
    });
    return this;
  }

  type(inputType: string): this {
    this.strategies.push({
      type: 'type',
      find: async () => await this.page.$(`input[type="${inputType}"]`)
    });
    return this;
  }

  role(role: string): this {
    this.strategies.push({
      type: 'role',
      find: async () => await this.page.$(`[role="${role}"]`)
    });
    return this;
  }

  testid(testid: string): this {
    this.strategies.push({
      type: 'testid',
      find: async () => await this.page.$(`[data-testid="${testid}"]`)
    });
    return this;
  }

  name(name: string): this {
    this.strategies.push({
      type: 'name',
      find: async () => await this.page.$(`[name="${name}"]`)
    });
    return this;
  }

  /**
   * 查找元素（尝试所有策略）
   */
  async find(timeout: number = 10000): Promise<ElementHandle | null> {
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      for (const strategy of this.strategies) {
        try {
          const element = await strategy.find();
          if (element && element.asElement && element.asElement()) {
            return element.asElement();
          }
          if (element) {
            return element;
          }
        } catch (error) {
          // 继续尝试下一个策略
          continue;
        }
      }

      // 等待一小段时间再重试
      await new Promise(resolve => setTimeout(resolve, 500));
    }

    return null;
  }
}

export default SmartSelector;
