
import { useStore } from '~/stores/index'
export default defineNuxtPlugin(() => {
  const store = useStore()

  //==========Find & filter=================
  const find = function(arr, obj, attr) {
    const keys = Object.keys(obj)
    let found = arr.find(v=>{
      let valid = true
      keys.map(key=>{
        let val = obj[key]
        if(valid===false) return false
        else if(Array.isArray(val)) {
          if(val.findIndex(x=>x===v[key]) <0) valid = false
        } else if(!(v[key]===val)) valid = false
      })
      return valid
    })
    return found? (attr? found[attr] : found) : undefined
  }

  const findIndex = function(arr, obj) {
    const keys = Object.keys(obj)
    return arr.findIndex(v=>{
      let valid = true
      keys.map(key=>{
        let val = obj[key]
        if(valid===false) return false
        else if(Array.isArray(val)) {
          if(val.findIndex(x=>x===v[key]) <0) valid = false
        } else if(!(v[key]===val)) valid = false
      })
      return valid
    })
  }

  const filter = function(arr, obj, attr) {
    const keys = Object.keys(obj)
    let rows = arr.filter(v=>{
      let valid = true
      keys.map(key=>{
        let val = obj[key]
        if(valid===false) return false
        else if(Array.isArray(val)) {
          if(val.findIndex(x=>x===v[key]) <0) valid = false
        } else if(!(v[key]===val)) valid = false
      })
      return valid
    })
    return attr? rows.map(v=>v[attr]) : rows
  }

  //=========Empty & copy============
  const id = function() {
    return Math.random().toString(36).substring(2, 12).toUpperCase()
  }

  const empty = function(val) {
    return val === undefined || val === null || val === ''
  }

  const copy = function(val) {
    if (empty(val)) return val
    return JSON.parse(JSON.stringify(val))
  }

  const clone = function(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
      return obj;
    if (obj instanceof Date)
      var temp = new obj.constructor(); //or new Date(obj);
    else
      var temp = obj.constructor();
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        obj['isActiveClone'] = null;
        temp[key] = clone(obj[key]);
        delete obj['isActiveClone'];
      }
    }
    return temp
  }

  const remove = function(arr, idx) {
    arr.splice(idx, 1)
  }

  const stripHtml = function(html, length) {
    if(!html) return null
    else if(typeof html!=='string') return html
    if(html? html.indexOf('<')<0 : false) {
      return length? (html.length > length? html.substring(0, length) + '...' : html ) : html
    }
    var tmp = document.createElement("DIV")
    tmp.innerHTML = html
    var val = tmp.textContent || tmp.innerText || ""
    return length? (val.length > length? val.substring(0, length) + '...' : val ) : val
  }

  //==========Convert=================
  const isNumber = function(val) {
    if(empty(val)) return false
    val = val.toString().replace(/,/g, "")
    return !isNaN(val)
  }

  const numtoString = function(val, config = undefined) {
    if (empty(val)) return '0';
    val = val.toString().replace(/,/g, "");
    if (isNaN(val)) return;

    const defaultConfig = { 
      type: 'vi-VN', 
      decimal: undefined,
      mindecimal: undefined,
      hasUnit: false 
    };

    const finalConfig = { ...defaultConfig, ...config };
    const { type, decimal, mindecimal, hasUnit } = finalConfig;

    const options = {};
    if (typeof decimal === 'number') options.maximumFractionDigits = decimal || 0;
    if (mindecimal) options.minimumFractionDigits = mindecimal;
    const unit = hasUnit ? ' đ' : '';
    return Number(val).toLocaleString(type, typeof decimal === 'number' ? options : undefined) + unit;
  }

  const formatNumber = function(val) {
    if(empty(val)) return
    val = val.toString().replace(/[,\.]/g, "")
    if (val.indexOf('%') >0) {
      val = val.replace(/%/g, "")
      return isNaN(val)? undefined : Number(val)/100
    }
    return isNaN(val)? undefined : Number(val)
  }

  const formatUnit = function(val, unit, decimal, string, mindecimal) {
    val = formatNumber(val)
    if(val===undefined) return
    let percentage = (unit===0.01 || unit==="0.01")? '%' : ''
    val = unit? val/Number(unit) : val
    let f = {maximumFractionDigits: decimal || 0}
    if(mindecimal) f['minimumFractionDigits'] = mindecimal
    return string? (val.toLocaleString('en-EN', f) + percentage) : (val).toFixed(decimal)
  }

//==========Calculate=================
  const calc = function(fn) {
    return new Function('return ' + fn)()
  }

  const calculate = function(row, tags, formula, decimal, unit) {
    let val = this.$copy(formula)
    let valid = 0
    tags.forEach(v => {
      let myRegExp = new RegExp(v, 'g')
      let res = this.$formatNumber(row[v])
      if(res) valid = 1
      val = val.replace(myRegExp, `(${res || 0})`)
    })
    if(valid===0) return {success: false, value : undefined} //all values is null
    //calculate
    try {
      let value = this.$calc(val)
      if(isNaN(value) || value===Number.POSITIVE_INFINITY || value===Number.NEGATIVE_INFINITY) {
        var result = {success: false, value : value}
      } else {
        value = (value===true || value===false)? value : formatUnit(value, unit, decimal, true, decimal)
        var result = {success: true, value: value}
      }
    }
    catch(err) {
      var result = {success: false, value : undefined}
    }
    return result
  }

  const calculateFunc = function(row, cols, func, decimal, unit) { 
    let value
    let arr1 = cols.map(v=>this.$formatNumber(row[v]))
    let arr = arr1.filter(v=>v)
    if(arr.length===0) return {success: false, value : undefined}
    if(func==='max') value = Math.max(...arr)
    else if(func==='min') value = Math.min(...arr)
    else if(func==='sum') value = arr.reduce((a, b) => a + b, 0)
    else if(func==='avg') {
      let total =  arr.reduce((a, b) => a + b, 0)
      value = total / cols.length
    }
    if(!value) return {success: false, value: undefined}
    value = formatUnit(value, unit, decimal, true, decimal)
    return {success: true, value: value}
  }

  const calculateData = function(data, fields) {
    let arr = this.$copy(fields.filter(h=>h.formula))
    if(arr.length===0) return data
    let arr1 = arr.filter(v=>v.func)
    arr1.map(v=>{
      if(v.vals.indexOf(':')>=0) {
        let arr2 = v.vals.toLowerCase().replaceAll('c', '').split(':')
        let cols = []
        for (let i = parseInt(arr2[0]); i <= parseInt(arr2[1]); i++) {
          let field = fields.length>i? fields[i] : undefined
          if(field? (field.format==='number' && field.name!==v.name) : false) cols.push(field.name) 
        }
        v.cols = cols
      } else {
        let arr2 = v.vals.toLowerCase().replaceAll('c', '').split(',')
        let cols = []
        arr2.map(v=>{
          let i = parseInt(v)
          let field = fields.length>i? fields[i] : undefined
          if(field? (field.format==='number' && field.name!==v.name) : false) cols.push(field.name) 
        })
        v.cols = cols
      }
    })
    arr = multiSort(arr, {level: 'asc'})
    let copy = data
    copy.map(v=>{
      arr.map(x=>{
        if(x.func) {
          let res = calculateFunc(v, x.cols, x.func, x.decimal, x.unit)
          if(res? res.success : false) v[x.name] = res.value
        } else {
          let res = calculate(v, x.tags, x.formula, x.decimal, x.unit)
          if(res? res.success : false) v[x.name] = res.value
        }
      })
    })
    return copy
  }

  const summary = function(arr, fields, type) {
    let obj = {}
    if(type==='total') {
      fields.map(x=> obj[x] = arr.map(v=>v[x]? v[x] : 0).reduce((a, b) => a + b, 0))
    } else if(type==='min') {
      fields.map(x=>obj[x] = Math.min(...arr.map(v=>v[x])))
    }
    else if(type==='max') {
      fields.map(x=>obj[x] = Math.max(...arr.map(v=>v[x])))
    }
    else if(type==='count') {
      fields.map(x=>obj[x] = arr.map(v=>!empty(v[x])).length)
    }
    return obj
  }

//====================Array====================
  const formatArray = function(data, fields) {
    let args = fields.filter(v=>v.format==='number')
    data.map(v=>{
      args.map(x=>{
        v[x.name] = empty(v[x.name])? undefined : formatUnit(v[x.name], x.unit, x.decimal, true, x.decimal)
      })
    })
    return data
  }

  const unique = function(arr, keyProps) {
    const kvArray = arr.map(entry => {
    const key = keyProps.map(k => entry[k]).join('|');
    return [key, entry];
    });
    const map = new Map(kvArray);
    return Array.from(map.values());
  }
  
  const arrayMove = function(arr, old_index, new_index) {
    if (new_index >= arr.length) {
      var k = new_index - arr.length + 1;
      while (k--) {
        arr.push(undefined);
      }
    }
    arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
    return arr; // for testing
  }

  const multiSort = function(array, sortObject = {}, format = {}) {
    const sortKeys = Object.keys(sortObject)

    // Return array if no sort object is supplied.
    if (!sortKeys.length) return array

    // Change the values of the sortObject keys to -1, 0, or 1.
    for (let key in sortObject)
        sortObject[key] = sortObject[key] === 'desc' || sortObject[key] === -1 ? -1 : (sortObject[key] === 'skip' || sortObject[key] === 0 ? 0 : 1)

    const keySort = (a, b, direction) => {
        direction = direction !== null ? direction : 1
        if (a === b) return 0

        // If b > a, multiply by -1 to get the reverse direction.
        return a > b ? direction : -1 * direction;
    }

    return array.sort((a, b) => {
      let sorted = 0, index = 0

      // Loop until sorted (-1 or 1) or until the sort keys have been processed.
      while (sorted === 0 && index < sortKeys.length) {
        const key = sortKeys[index]
        if (key) {
          const direction = sortObject[key]
          let val1 = format[key]==='number'? (empty(a[key])? 0 :  this.$formatNumber(a[key])) : a[key]
          let val2 = format[key]==='number'? (empty(b[key])? 0 :  this.$formatNumber(b[key])) : b[key]
            sorted = keySort(val1, val2, direction)
            index++
          }
        }
      return sorted
    })
  }

  //======================Fields====================
  const createField = function (name, label, format, show, minwidth, template) {
    let field = { name, label, format, show, minwidth, template };
    if (format === 'number') {
      field.unit = '1'
      field.textalign = 'right'
    }
    return field
  }

  const updateFields = function(pagename, field, action) {
    let pagedata = store[pagename]
    let copy = this.$copy(pagedata.fields)
    let idx = this.$findIndex(copy, {name: field.name})
    if(action==='delete') {
      this.$delete(copy, idx)
      if(pagedata.filters? pagedata.filters.length>0 : false) {
        let index = this.$findIndex(pagedata.filters, {name: field.name})
        if(index>=0) {
          let copyFilter = this.$copy(this.pagedata.filters)
          this.$delete(copyFilter, index)
          this.$store.commit('updateState', {name: pagename, key: 'filterby', data: copyFilter})
        }
      }
    } else {
      idx>=0? copy[idx] = field : copy.push(field)
    }
    this.$store.commit("updateState", {name: pagename, key: "fields", data: copy})      
  }

  const updateSeriesFields = function(fields) {
    if (!Array.isArray(fields)) {
      fields = Object.values(fields);
    }
    
    fields.filter(v=>v.series).map(field=>{
      let obj = field.api==='finitem'? this.$findPeriod(field.series, 'period') : this.$findPeriod(field.series)
      field.period = field.api==='finitem'? obj.code : obj
      let idx = field.label.indexOf('[')
      field.label = (idx>0? field.label.substring(0, idx) : field.label) + '[' + (field.api==='finitem'? obj.show : obj)  + ']'
    })
    return fields
  }
  
  const updateSeriesFilters = function(filters, fields) {
    if(fields.filter(v=>v.series).length===0) return filters
    filters.map(v=>{
      let found = fields.find(x=>x.name===v.name)
      if(found? found.series : false) {
        v.label = found.label
      }
    })
    return filters
  }

  //======================Export=============================== 
  const exportExcel = function(data, filename, fields) {
    var _filename = filename + '.xlsx'
    let list = []
    data.map(v=>{
      let ele = {}
      fields.map(x=>{
        let label = stripHtml(x.label)
        ele[label] = v[x.name]
      })
      list.push(ele)
    })
    var XLSX = require('xlsx')
    //workBook class
    function Workbook() {
      if(!(this instanceof Workbook)) return new Workbook()
      this.SheetNames = []
      this.Sheets = {}
    }
    var exportBook = new Workbook();
    var worksheet =  XLSX.utils.json_to_sheet(list)
    exportBook.SheetNames.push('sheet1')
    exportBook.Sheets.sheet1 = worksheet
    XLSX.writeFile(exportBook, _filename)        
  }

  return {
    provide: {
      find,
      findIndex,
      filter,
      id,
      empty,
      copy,
      clone,
      remove,
      stripHtml,
      isNumber,
      numtoString,
      formatNumber,
      formatUnit,
      calc,
      calculate,
      calculateFunc,
      calculateData,
      summary,
      formatArray,
      unique,
      arrayMove,
      multiSort,
      createField,
      updateFields,
      updateSeriesFields,
      updateSeriesFilters,
      exportExcel
    }
  }
})