/**
 * Created at 2018/2/28.
 * @Author Ling.
 * @Email i@zeroling.com
 */
import React, { Component } from 'react';
import { Link, withRouter } from 'react-router';
import { Input } from '@icedesign/base';
import Searcher from './Searcher';
import {
  getVueMaterials,
  getReactMaterials,
  getDocs,
  getBaseComponentDocs,
  getBizComponentDocs,
} from '../../service/database';
import { getJsonmlString } from './utils';
import { join } from 'path';
import './Search.scss';

const appRoutePrefix = window.routePrefix;
const searcher = new Searcher();

@withRouter
export default class extends Component {
  state = {
    text: '',
    showResultPanel: false,
    searchResult: [],
    focus: false,
  };

  /**
   * 准备数据
   */
  _dataCache = null;
  _preparing = false;
  _preparingResolvers = [];
  prepareData = () => {
    if (this._preparing) {
      return new Promise((resolve, reject) => {
        this._preparingResolvers.push({ resolve, reject });
      });
    }

    this._preparing = true;
    return Promise.all([
      getReactMaterials(),
      getVueMaterials(),
      getDocs(),
      getBaseComponentDocs(),
      getBizComponentDocs(),
    ])
      .then(([reactMaterials, vueMaterials, docs, baseComp, bizComp]) => {
        const blockArticles = [];
        reactMaterials.blocks &&
          reactMaterials.blocks.forEach((block) => {
            blockArticles.push({
              title: `区块 - ${block.title}`,
              keywords: `${block.title} ${block.categories.join(
                ' '
              )} ${name.split('-')}`,
              content: block.description || block.title,
            });
          });

        const layoutArticles = [];
        const docArticles = [];
        const componentArticles = [];

        searcher.registerArticles([
          ...blockArticles,
          ...layoutArticles,
          ...docArticles,
          ...componentArticles,
        ]);

        docs.forEach((doc) => {
          doc.rawText = getJsonmlString(doc.jsonml);
        });
        this._dataCache = {
          blocks: reactMaterials.blocks,
          layouts: reactMaterials.layouts,
          scaffolds: reactMaterials.scaffolds,
          docs,
          baseComp,
          bizComp,
        };
        this._preparing = false;
        this._preparingResolvers.forEach(({ resolve }) => {
          resolve(this._dataCache);
        });
        return this._dataCache;
      })
      .then(({ blocks, layouts, scaffolds, docs, baseComp, bizComp }) => {
        const indexData = {};
        /**
         * 遍历物料
         */
        function createIter(type) {
          return (marterial) => {
            const descriptor = {};

            let path;
            if (type === 'block') {
              path = `/ice/${type}/${marterial.name}?type=react`;
            } else {
              path = `/${type}/${marterial.name}`;
            }

            descriptor.title = marterial.title || marterial.name;
            descriptor.body = marterial.description || descriptor.title;
            switch (type) {
              case 'block':
                descriptor.type = '物料 - 区块';
                break;
              case 'layout':
                descriptor.type = '物料 - 布局';
                break;
              case 'scaffold':
                path = marterial.homepage;
                // 新页面打开
                descriptor.external = true;
                descriptor.type = '物料 - 模板';
                if (descriptor.body !== descriptor.title) {
                  descriptor.body = descriptor.title + '：' + descriptor.body;
                }
                break;
            }

            descriptor.path = path;
            indexData[path] = descriptor;
          };
        }

        blocks.forEach(createIter('block'));
        layouts.forEach(createIter('layout'));
        scaffolds.forEach(createIter('scaffold'));

        // 遍历组件
        [...baseComp, ...bizComp].forEach((comp) => {
          const path = join(
            appRoutePrefix,
            'component',
            comp.name.toLowerCase()
          );
          const rawTexts = [comp.title, comp.body, ...comp.methodREADME];
          comp.subComponentsREADME &&
            comp.subComponentsREADME.forEach((subComp) => {
              rawTexts.push(subComp.name + subComp.propsREADME);
            });
          indexData[path] = {
            path,
            title: comp.name + ' ' + comp.title,
            body: rawTexts.join('\n'),
            type: '物料 - 组件',
          };
        });

        // 遍历文档
        docs.forEach((doc) => {
          const path = join(appRoutePrefix, 'docs', doc.path);
          indexData[path] = {
            path,
            title: doc.title,
            body: doc.rawText,
            type: '文档' + (doc.category ? ' - ' + doc.category : ''),
          };
        });

        return indexData;
      });
  };

  handleInputChange = (text) => {
    this.setState({ text });

    this.prepareData().then((indexData) => {
      clearTimeout(this.searchTimer);
      this.searchTimer = setTimeout(() => {
        const searchResult = this.doSearch(text, indexData);
        // searcher.searchICE(text).then((result) => {
        //   console.log('engine result', result);
        // });
        this.setState({
          searchResult: searchResult || [],
          showResultPanel: !!text,
        });
      }, 150);
    });
  };

  doSearch = (keyword, indexData) => {
    if (keyword == null || keyword.trim() === '') {
      return [];
    }

    // 显示字符串的数量
    const MAX_DESCRIPTION_SIZE = 80;
    const MAX_RESULT_COUNT = 5;
    const results = [];
    let index;

    for (let page in indexData) {
      if (results.length >= MAX_RESULT_COUNT) {
        break;
      }
      if (
        (index = (indexData[page].title + indexData[page].body)
          .toLowerCase()
          .indexOf(keyword.toLowerCase())) !== -1
      ) {
        let body = indexData[page].body.substr(
          Math.max(0, index - 50),
          MAX_DESCRIPTION_SIZE
        );
        const match = new RegExp('(' + escapeReg(keyword) + ')', 'gi');
        const children = [];
        body.split(match).forEach((item, idx) => {
          if (item.toLowerCase() === keyword.toLowerCase()) {
            children.push(
              <span key={item + idx} className="search-highlight-keyword">
                {item}
              </span>
            );
          } else {
            children.push(item);
          }
        });
        body = <span>{children}</span>;

        results.push({
          ...indexData[page],
          url: page,
          type: indexData[page].type,
          title: indexData[page].title,
          body,
        });
      }
    }

    return results;
  };

  clearState = () => {
    this.setState({
      text: '',
      searchResult: [],
      focus: false,
    });
  };

  renderSearchResult = (results) => {
    if (!Array.isArray(results) || results.length === 0) {
      return <div className="search-result-item">暂无匹配项</div>;
    }

    return results.map((result, idx) => {
      const key = result.title + idx;
      if (result.external) {
        return (
          <a
            key={key}
            href={result.url}
            target="_blank"
            className="search-result-item"
            onClick={this.clearState}
          >
            <div className="search-result-title">
              「{result.type}」 {result.title}
            </div>
            {result.body}
          </a>
        );
      } else {
        return (
          <Link
            key={key}
            to={result.url}
            className="search-result-item"
            onClick={this.clearState}
          >
            <div className="search-result-title">
              「{result.type}」 {result.title}
            </div>
            {result.body}
          </Link>
        );
      }
    });
  };

  render() {
    const { router } = this.props;
    const { text, showResultPanel, searchResult, focus } = this.state;
    return (
      <div className="search-container">
        <Input
          placeholder="全局搜索"
          onChange={this.handleInputChange}
          style={{
            color:
              router.location.pathname === '/' ||
              router.location.pathname === '/ice/'
                ? '#fff'
                : '#333',
          }}
          onBlur={() => {
            setTimeout(() => {
              this.setState({ focus: false });
            }, 200);
          }}
          onClick={() => {
            this.setState({ focus: true });
          }}
          value={text}
        />
        {showResultPanel && (
          <div className={`search-result-panel ${focus ? 'active' : ''}`}>
            {this.renderSearchResult(searchResult)}
          </div>
        )}
      </div>
    );
  }
}

function escapeReg(keyword) {
  // escape regexp prevserve word
  return String(keyword).replace(/([\*\.\?\+\$\^\[\]\(\)\{\}\|\/\\])/g, '\\$1');
}
