import React, { Fragment, useEffect, useRef, useState } from 'react';
import { getUploadActionUrl, getUrl } from 'educoder';
import ResizeObserver from 'resize-observer-polyfill';
import { getImageUrl } from 'educoder';
import axios from 'axios';
import '../../courses/css/Courses.css';
import './css/TPMchallengesnew.css';
import 'codemirror/lib/codemirror.css';
import './css/newquestion.css';
const $ = window.$

const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];

const NULL_CH = '▁';

function md_add_data(k, mdu, d) {
  window.sessionStorage.setItem(k + mdu, d);
}

// 清空保存的数据
function md_clear_data(k, mdu, id) {
  window.sessionStorage.removeItem(k + mdu);
  var id1 = "#e_tip_" + id;
  var id2 = "#e_tips_" + id;
  if (k == 'content') {
    $(id2).html("　");
  } else {
    $(id1).html("　");
  }
}

window.md_clear_data = md_clear_data
function md_rec_data(k, mdu, id) {
  if (window.sessionStorage.getItem(k + mdu) !== null) {
    var editor = $("#e_tips_" + id).data('editor');
    editor.setValue(window.sessionStorage.getItem(k + mdu));
    // /shixuns/b5hjq9zm/challenges/3977/tab=3  setValue可能导致editor样式问题
    md_clear_data(k, mdu, id);
  }
}
window.md_rec_data = md_rec_data;

function md_elocalStorage(editor, mdu, id) {
  let oc = window.sessionStorage.getItem('content' + mdu)
  if (oc !== null && oc !== editor.getValue()) {
    $("#e_tips_" + id).data('editor', editor);
    let h = '您上次有已保存的数据，是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\'' + mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\'' + mdu + '\',\'' + id + '\')">不恢复</a>';
    $("#e_tips_" + id).html(h)
  }
  let tid = setInterval(function () {
    let d = new Date();
    let h = d.getHours();
    let m = d.getMinutes();
    let s = d.getSeconds();
    h = h < 10 ? '0' + h : h;
    m = m < 10 ? '0' + m : m;
    s = s < 10 ? '0' + s : s;

    if (editor.getValue().trim() !== "") {
      md_add_data("content", mdu, editor.getValue());
      let id2 = "#e_tips_" + id;

      let textStart = " 数据已于 ";
      let text = textStart + h + ':' + m + ':' + s + " 保存 ";
      // 占位符   
      let oldHtml = $(id2).html();
      if (oldHtml && oldHtml !== '　' && oldHtml.startsWith(textStart) === false) {
        $(id2).html(oldHtml.split('　(')[0] + `　(${text})`);
      } else {
        $(id2).html(text);
      }
    }
  }, 10000)
  return tid
}

export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => {
  
  const editorEl = useRef();
  const resizeBarEl = useRef();
  const [editorInstance, setEditorInstance] = useState();
  const [atWhoVisible, setAtWhoVisible] = useState(false);
  const [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
  //调用member.json接口获取到的用户列表
  const [users, setUsers] = useState([]);
  //可以@的全部用户
  const [allUsers, setAllUsers] = useState([]);
  const atWhoLoginList = useRef([]);
  const atWhoVisibleRef = useRef(false);
  const containerId = `mdEditor_${mdID}`;
  const editorBodyId = `mdEditors_${mdID}`;
  const tipId = `e_tips_mdEditor_${mdID}`;

  useEffect(()=>{
    //请求members接口获取全部可@列表
    isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
      if(response.data.total_count !== 0){
        setAllUsers(response.data.users);
        setUsers(response.data.users);
      }
    })
    //点击其他地方关闭弹框
    document.addEventListener('click',()=>{
      atWhoVisibleRef.current = false;
      setAtWhoVisible(false);
    })
  },[])

  function onLayout() {
    let ro;
    if (editorEl.current) {
      ro = new ResizeObserver(entries => {
        for (let entry of entries) {
          if (entry.target.offsetHeight > 0 || entry.target.offsetWidth > 0) {
            editorInstance.resize();
            editorInstance.cm.refresh();
            editorInstance.cm.focus();
          }
        }
      })
      ro.observe(editorEl.current);
    }
    return ro;
  }

  function selectAtWho(username){
    atWhoVisibleRef.current = false;
    setAtWhoVisible(false);
    const cm = editorInstance.cm;
    //获取鼠标所在行的行数和ch
    const cursor = cm.doc.getCursor();
    const line = cursor.line;//行
    const ch = cursor.ch;//列
    const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@");
    let selectUserLogin = undefined;
    users.map((item)=>{
      item.username === username && (selectUserLogin = item.login);
    })
    //替换内容
    cm.replaceRange("[@"+username+"]"+`(/${selectUserLogin}) `,{line,ch:startIndex},{line,ch});
    //鼠标聚焦
    cm.focus();
    //将此user的login存储到atWhoLoginList集合中
    const list = new Set(atWhoLoginList.current);
    list.add(selectUserLogin);
    atWhoLoginList.current = Array.from(list);
    setAtWhoLoginListState(Array.from(list));
  }

  function onMouseOver(key){
    document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who");
    document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active");
  }

  //markdown编辑器中输入的键盘监听事件
  function mdKeyDown(e){
    if (e.shiftKey && e.code === "Digit2") {
      // 输入@键后在对应的位置显示可选的项目成员
      atWhoVisibleRef.current = true;
      setAtWhoVisible(true);
      //获取光标位置
      const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
      //设置弹框位置
      const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
      const newLeft = placeholder === "添加评论..." ? 80: 20;
      document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px";
      document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px";
    }
    //处理本来@了某人 -> 删掉 -> 撤回 的情况
    if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){
      const codemirror  = editorInstance.cm;
      let value = codemirror.getValue();
      //处理初始内容就自带@谁的情况
      if(initValue){
        const del = [];
        allUsers.map(item=>{
          if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
            //初始内容中有符合@+名字的格式并且当前内容未删除初始内容
            del[del.length] = `[@${item.username}](/${item.login})`;
          }
        })
        del.length!=0 && del.map(str=>{
          value = value.replace(str,"");
        })
      }
      //判断value是否包含@符号
      value.indexOf("@") != -1 && allUsers.map(item =>{
        if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){
          //将此user的login存储到atWhoLoginList集合中
          const list = new Set(atWhoLoginList.current);
          list.add(item.login);
          atWhoLoginList.current = Array.from(list);
          setAtWhoLoginListState(Array.from(list));
        }
      })
    }
  }

  useEffect(()=>{
    changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
  },[atWhoLoginListState])

  const atWhoList = (
    <div className="at_who_list" id="at_who_list" >
      {users && users.map((item,key)=>{
        return(
          <div key={key} className={`at_who ${key===0 && `active`}`} onClick={()=>{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}>
            {item.image_url && <img src={getImageUrl(`/${item.image_url}`)}></img>}
            <span>{item.username}</span>
          </div>
        )
      })}
    </div>
  )

  useEffect(() => {
    if (editorInstance) {
      return
    }
    if (!startInit) { return }
    const editor_instance = window.editormd(containerId, {
      width,
      height,
      path: getUrl("/editormd/lib/"),
      markdown: initValue,
      syncScrolling: "single",
      tex: true,
      tocm: true,
      emoji: !!emoji,
      taskList: true,
      codeFold: true,
      searchReplace: true,
      htmlDecode: "style,script,iframe",
      sequenceDiagram: true,
      autoFocus: false,
      watch: watch === undefined ? true : watch,

      saveHTMLToTextarea: true,
      dialogMaskOpacity: 0.6,
      placeholder: placeholder,
      imageUpload: true,
      imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
      imageUploadURL: getUploadActionUrl(),
      toolbarIcons: function () {
        return showNullButton ? [...mdIcons, 'null-button'] : mdIcons
      },
      toolbarIconsClass: {
        "line-break": "fa-minus",
        "fullScreen":"iconfont icon-fangdaicon font-14"
      },
      toolbarCustomIcons: {
        "inline-latex": "<a title='行内公式' class='latex' ><i name='inline-latex' class='fa iconfont icon-hangneigongshi font-14'></i></a>",
        "latex": "<a title='多行公式'  class='latex' ><i name='latex' class='fa iconfont icon-duohanggongshi font-16'></i></a>",
        "null-button": "<a class='pr' title='增加填空'><i class='border-left'><span></span></i><span name='null-button' class='fa fillTip'>点击插入填空项</span><i class='iconfont fa icon-edit font-16' name='null-button'></i></a>",
      },
      toolbarHandlers: {
        "line-break": function (cm, icon, cursor, selection) {
          cm.replaceSelection("<br/>")
        },
        "null-button": function (cm, icon, cursor, selection) {
          if (selection === "") {
            cm.setCursor(cursor.line, cursor.ch + 1)
          }
          cm.replaceSelection(NULL_CH)
        },
        "fullScreen":function(cm,icon,cursor,selection){
          icon.addClass("none");
          console.log(cm,icon)
        },
        "inline-latex": function (cm, icon, cursor, selection) {
          cm.replaceSelection("$$" + selection + "$$");
          cm.setCursor(cursor.line, cursor.ch + 2);
          cm.focus()
        },
        "latex": function (cm, icon, cursor, selection) {
          cm.replaceSelection("```latex\n\n" + selection + "```");
          cm.setCursor(cursor.line + 1, 0);
          cm.focus()
        },
      },
      lang: {
        toolbar: {
          "latex": "多行公式",
          "line-break": "换行",
          "fullScreen":"开启全屏"
        }
      },
      onload: function () {
        setEditorInstance(this)
      }
    })


  }, [containerId, editorInstance, startInit])

  const cmEl = editorInstance && editorInstance.cm

  useEffect(()=>{
    if(atWhoVisibleRef.current){
      // 添加上下键、enter键监听事件
      cmEl.addKeyMap({
        'Up':()=>{
          const atWhoListDiv = document.getElementById("at_who_list");
          const atWhoDivs = document.getElementsByClassName("at_who");
          let index;
          for(let i = 0; i<atWhoDivs.length;i++){
            atWhoDivs[i].className === "at_who active" && (index = i);
          }
          if(index>0){
            index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40)
            atWhoDivs[index].className = "at_who";
            atWhoDivs[index-1].className = "at_who active";
          }
        },
        'Down':()=>{
          const atWhoListDiv = document.getElementById("at_who_list");
          const atWhoDivs = document.getElementsByClassName("at_who");
          let index;
          for(let i = 0; i<atWhoDivs.length;i++){
            atWhoDivs[i].className === "at_who active" && (index = i);
          }
          if(index<atWhoDivs.length-1){
            index >=3 && (atWhoListDiv.scrollTop +=40)
            atWhoDivs[index].className = "at_who";
            atWhoDivs[index+1].className = "at_who active";
          }
        },
        'Enter':()=>{
          //找到classname为at_who active的div，执行click事件
          if(document.getElementsByClassName("at_who active")[0]){
            document.getElementsByClassName("at_who active")[0].click()
          }else{
            const cm = editorInstance.cm;
            const cursor = cm.doc.getCursor();
            const line = cursor.line;//行
            const ch = cursor.ch;//列
            //添加换行
            cm.replaceRange("\n",{line,ch},{line,ch});
            setAtWhoVisible(false);
            atWhoVisibleRef.current = false;
          }
        }
      })
    } else {
      //移除上下、enter键监听
      cmEl && cmEl.removeKeyMap();
    }
  },[atWhoVisible])

  useEffect(()=>{
    //当users数组发生变化时改变框的位置
    if(atWhoVisibleRef.current && users){
      //获取光标位置
      const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
      //设置弹框位置
      const newLeft = placeholder === "添加评论..."? 80: 10;
      document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft)+"px";
    }
  },[users])

  useEffect(() => {
    if (cmEl) {
      let tid = null
      let ro
      if (onCMBlur) {
        editorInstance.cm.on('blur', () => { onCMBlur(editorInstance.getValue()) })
      }
      if (onCMBeforeChange) {
        editorInstance.cm.on('beforeChange', (cm, change) => {
          onCMBeforeChange(cm, change)
        })
      }
      if (!noStorage) {
        tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId)
      }
      //isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
      //绑定@事件
      isCanAtme && editorInstance.cm.on("focus", () => {
        document.addEventListener("keydown", mdKeyDown);
      });
      isCanAtme && editorInstance.cm.on("blur", () => {
        document.removeEventListener("keydown",mdKeyDown);
      });
      editorInstance.cm.on("change", (cm) => {
        //调用父组件的onchange方法，将输入内容传入父级组件
        onChange && onChange(cm.getValue());
        if(atWhoVisibleRef.current){
          //搜索用户(弹框之后用户输入用户名信息)
          const cur = cm.doc.getCursor();
          const line = cur.line;
          const ch = cur.ch;
          let rangeCont = cmEl.getRange({line,ch:0},{line,ch});
          //处理已经弹出列表框，但用户删除@符号
          if(rangeCont.indexOf("@")===-1){
            setAtWhoVisible(false);
            atWhoVisibleRef.current = false;
          }else{
            rangeCont = rangeCont.substring(rangeCont.lastIndexOf("@")+1);
            rangeCont ? axios.get(`/${owner}/${projectsId}/members.json`,{
              params: {
                search: rangeCont,
              },
            }).then(response=>{
              if(response && response.data && response.data.total_count !== 0){
                setUsers(response.data.users);
              }else{
                setUsers(undefined);
              }
            }):setUsers(allUsers)
          }
        }

        //当内容发生改变并且有已@列表时
        if(atWhoLoginList.current.length != 0){
          const codemirror  = editorInstance.cm;
          //startValue：触发change方法时的内容，value：处理了初始内容带@用户的情况
          let startValue = codemirror.getValue();
          let value = codemirror.getValue();
          //处理初始内容就自带@谁的情况
          if(initValue){
            const del = [];
            allUsers.map(item=>{
              if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
                //初始内容中有符合@+名字的格式并且当前内容未删除初始内容
                del[del.length] = `[@${item.username}](/${item.login})`;
              }
            })
            del.length!=0 && del.map(str=>{
              value = value.replace(str,"");
            })
          }
          //以username为主键，login为value的map集合
          let atWhoMap = new Map();
          Array.from(atWhoLoginList.current).map(item=>{
            allUsers.map(i=>{
              if(i.login === item){
                atWhoMap.set(i.username,i.login);
              }
            })
          });
          const cursor = codemirror.doc.getCursor();
          const line = cursor.line;
          const ch = cursor.ch;
          //处理全部内容中不包含“@”的情况
          if(value.indexOf("@") === -1){
            //markdown嵌套的链接删掉
            // Array.from(atWhoMap.keys()).map(username=>{
            //   startValue = startValue.replaceAll(`[${username}](/${atWhoMap.get(username)}) `,username);
            // })
            //替换全部内容
            // codemirror.setValue(startValue);
            //全部内容已经有要@的列表，但是没有@符号 -> 清空@集合
            atWhoLoginList.current = [];
            setAtWhoLoginListState([]);
          }

          //截取第一个字符到光标的内容
          const curAfterCont = codemirror.getRange({line,ch:0},{line,ch});
          const content = codemirror.getLine(line);
          //处理光标所在行 有“@”的情况
          if(content && content.indexOf("@") !== -1){
            Array.from(atWhoMap.keys()).map(username=>{
              //判断content是不是以列表中的某个username结尾
              const userCont = `[@${username}](/${atWhoMap.get(username)})`;
              //删除空格->选中@用户区域
              if(curAfterCont.endsWith(userCont)){
                codemirror.setSelection({line,ch:curAfterCont.lastIndexOf("@")-1},{line,ch});
              }
              //处理已经有@列表但是value中不包含完整[@用户名](/login)的情况
              if(value.indexOf(userCont)===-1){
                // //markdown嵌套的链接删掉,删[]、()的情况不用处理，markdown会自动认为不是链接
                // //找到[和)的index，将区域内容替换成[]包裹的内容
                // //光标之后的内容
                // const curLeterCont = codemirror.getRange({line,ch},{line,ch:content.length});
                // console.log('光标之后的内容curLeterCont',curLeterCont);
                // //删除用户名 -> ]在curLeterCont中
                // //删除login -> ]在curAfterCont中
                // const a = curAfterCont.lastIndexOf('[');
                // const b = curLeterCont.indexOf(')')
                // const c = curLeterCont.indexOf(']') === -1 ? curAfterCont.lastIndexOf(']') : curLeterCont.indexOf(']')+curAfterCont.length;
                // console.log('[',a,')',b,']',c);
                // const newCont = codemirror.getRange({line,ch:a+1},{line,ch:c});
                // console.log('newCont',newCont);
                // codemirror.replaceRange(newCont,{line,ch:a-1},{line,ch:b+curAfterCont.length+1})

                //符合情况->踢掉这个人 不给他发消息
                const list = new Set(atWhoLoginList.current);
                list.delete(atWhoMap.get(username));
                atWhoLoginList.current = Array.from(list);
                setAtWhoLoginListState(Array.from(list));
              }
            })
          }else{
            //处理所在行没有“@”的情况
            Array.from(atWhoMap.keys()).map(username=>{
              const userCont = `[@${username}](/${atWhoMap.get(username)})`;
              if(value.indexOf(userCont)===-1){
                //符合情况->踢掉这个人 不给他发消息
                const list = new Set(atWhoLoginList.current);
                list.delete(atWhoMap.get(username));
                atWhoLoginList.current = Array.from(list);
                setAtWhoLoginListState(Array.from(list));
              }
            })
          }
        }
      });
      ro = onLayout()
      return () => {
        if (!noStorage) {
          clearInterval(tid)
        }
        if (ro) {
          ro.unobserve(editorEl.current)
        }
      }
    }
  }, [cmEl])

  useEffect(() => {
    if (editorInstance && initValue !== undefined) {
      if (initValue !== null && initValue !== editorInstance.getValue()) {
        editorInstance.setValue(initValue.toString())
      }
    }
  }, [editorInstance, initValue, containerId])

  useEffect(() => {
    if (resizeBarEl.current) {
      let el = resizeBarEl.current
      let dragging = false
      let startY = 0  
      function onMouseDown(e) {
        dragging = true
        startY = e.pageY
      }
      function onMouseUp() {
        dragging = false
      }
      function onMouseMove(e) {
        if (dragging) {
          let delta = e.pageY - startY
          if (delta < 0) {
            delta = 0
          }
          if (delta > 300) {
            delta = 300
          }
          let resizeH = height + delta + 'px'
          editorInstance.resize('', resizeH)
        }
      }
      el.addEventListener('mousedown', onMouseDown)
      document.addEventListener('mousemove', onMouseMove)
      document.addEventListener('mouseup', onMouseUp)
      return () => {
        el.removeEventListener('mousedown', onMouseDown)
        document.removeEventListener('mousemove', onMouseMove)
        document.removeEventListener('mouseup', onMouseUp)
      }
    }
  }, [
    editorInstance, resizeBarEl
  ])

  return (
    <Fragment>
      {atWhoVisible && atWhoList}
      <div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
        <div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
          <textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
          <div className="CodeMirror cm-s-defualt"></div>
        </div>
      </div>
      {showResizeBar ? <a ref={resizeBarEl} className='editor-resize'></a> : null}
      <div className={"fr rememberTip"}>
        {noStorage === true ? null : <div id={tipId} className="edu-txt-right color-grey-cd font-12"></div>}
      </div>
    </Fragment>
  )
}