import {EventBusDemo}  from "../message/EventBusDemo";
import IStorageInterface from "../interface/IStorageInterface";
import $bus,{RuntimeEventBus} from "../message/RuntimeEventBus";
import { KeyValue } from "../types/KeyValue";
import ResponseUtils from "../utils/ResponseUtils";
import { isArray, isBackgroundScript, isFunctionAsync, isObject, isPopupScript, isString } from "../utils/Utils";

class StorageDataService extends EventBusDemo implements IStorageInterface{
  script:string='content';
  data:KeyValue={}
  keys:KeyValue={
    get:'_message_storage_data_service_get',
    set:'_message_storage_data_service_set',
    watch:'_message_storage_data_service_watch',
    listener:'_message_storage_data_service_listener',
  }
  watchKeys:Array<string>=[]//具体要监听的字段
  $runtimeBus:RuntimeEventBus=$bus;
  constructor(script?:string){
    super()
    let defaultScript='content'
    if(isBackgroundScript()){
      defaultScript='background'
    }else if(isPopupScript()){
      defaultScript='popup'
    }
    this.script=script||defaultScript;//当前脚本
    // this.$runtimeBus=new RuntimeEventBus()
    if(this.script==='background'){
      this.listener()
    }
    this.listenerWatch()
  }

  /**
   * 重写方法
   * @param name 
   * @param callback 
   * @returns 
   */
  $on(name: string, callback: Function):this {
    if(!name)return this;
    if(this.script==='background'){
      if(isString(name)){
        this.watchKeys.push(name)
      }
      return this;
    }
    this.$runtimeBus.$emitBackground(this.keys.watch,name)
    super.$on(name,callback);
    return this;
  }
  /**
   * 重写提交事件
   * @param type 消息类型
   * @param args 参数
   * @returns 
   */
  $emit(type:string, ...args:any):Promise<any>{
    const [oldVal,newVal]=args;
    if(!type){
      Promise.reject('错误字段')
    }
    if(this.script==='background'){
      const data={key:type,oldVal,newVal}
      this.$runtimeBus.$emitAllTabs(this.keys.listener,data)
    }
    return Promise.resolve('')
  }
  listenerWatch(){
    let _this=this;
    if(this.script==='background'){//后台监听其他页面提交过来的需要监听的key
      //仅支持每次监听一个key
      this.$runtimeBus.$on(this.keys.watch,(data:any,sender:chrome.runtime.MessageSender,sendResponse:any)=>{
        if(isString(data)){
          _this.watchKeys.push(data)
        }
        return true;
      })
    }else{//非background页面
      this.$runtimeBus.$on(this.keys.listener,(data:any,sender:chrome.runtime.MessageSender,sendResponse:any)=>{
        // console.log('addListener----',request,sender,sendResponse)
        //data:{key:监听字段,old:any,new:any}
        return new Promise((resolve,reject)=>{
          _this.handler(data,sender,sendResponse).then((res)=>{
            resolve(res)
          }).catch((err:any)=>{
            reject(err||'监听失败')
          })
        })
      })
    }
  }
  /**
   * 消息处理
   * @param {*} request:any
   * @param {*} sender:chrome.runtime.MessageSender 
   * @param {*} sendResponse:any
   */
  async handler(data:any,sender:chrome.runtime.MessageSender,sendResponse:any){
    let _this=this;
    //data:{key:监听字段,old:any,new:any}
    // console.log('listener------',data)
    const {key,oldVal,newVal}=data;
    if(!key){
      return;
    }
    let handler = _this._events.get(key)//所有该类型的函数集合
    if(!handler)return;//无任何监听则退出
      if (Array.isArray(handler)) {
        // 是数组，说明有多个监听者，需要依次触发里边的函数
        for (let i = 0; i < handler.length; ++i) {
          let res:any=''
          if(isFunctionAsync(handler)){
            handler[i].call(_this, oldVal,newVal,sender,sendResponse).then(sendResponse)
          }else{
            res=handler[i].call(_this, oldVal,newVal,sender,sendResponse)
            if(res){
              sendResponse(res)//发送返回值来实现$emit中Promise返回内容
            }
          }
        }
      } else {
        // 单个函数的情况直接触发即可
        // let res=await handler.call(_this, ...args,sender,sendResponse)
        let res:any=''
        if(isFunctionAsync(handler)){
          handler.call(_this, oldVal,newVal,sender,sendResponse).then(sendResponse)
        }else{
          res=handler.call(_this,oldVal,newVal,sender,sendResponse)
          if(res){
            sendResponse(res)//发送返回值来实现$emit中Promise返回内容
          }
        }
      }
  }


  listener(){
    let _this=this;
    this.$runtimeBus.$on(this.keys.get,(data:any,sender:chrome.runtime.MessageSender,sendResponse:any)=>{
      return new Promise((resolve,reject)=>{
        _this.get(data).then((res)=>{
          resolve(res)
        }).catch((err:any)=>{
          reject(err||'获取失败')
        })
      })
    })
    this.$runtimeBus.$on(this.keys.set,(data:any,sender:chrome.runtime.MessageSender,sendResponse:any)=>{
      return new Promise((resolve,reject)=>{
        if(!isObject(data)){
          reject('设置失败,data不是对象');
          return 
        }
        _this.set(data).then((res)=>{
          resolve(res)
        }).catch((err:any)=>{
          reject(err||'设置失败')
        })
      })
    })
  }

  /**
   * 根据key获取数据
   * @param {*} name 
   */
  get(name:string|Array<string>){
    if(this.script!=='background'){
      return this.$runtimeBus.$emitBackground(this.keys.get,name)
    }
    return new Promise((resolve,reject)=>{
      let result:KeyValue={}
      if(isString(name)){
        result[<string>name]=this.data[<string>name]
        resolve(result)
      }else if(Array.isArray(name)){
        name.forEach((key)=>{
          result[key]=this.data[key]
        })
        resolve(result);
      }else{
        reject('error name')
      }
    })
  }
  /**
   * 设置数据
   * @param {*} name 
   * @param {*} value 
   */
  set(name:string|Object,value?:any){
    if(this.script!=='background'){
      let data:KeyValue={}
      if(isObject(name)){
        data=<KeyValue>name;
      }else{
        data[<string>name]=value;
      }
      return this.$runtimeBus.$emitBackground(this.keys.set,data)
    }
    return new Promise((resolve,reject)=>{
      let data:KeyValue={}
      if(isString(name)){
        data[<string>name]=value
        this.checkWatch(data)
        this.data[<string>name]=value;
      }else if(isObject(name)){
        data=<KeyValue>name
        this.checkWatch(data)
        this.data=Object.assign(this.data,name)
      }else{
        reject('name error')
      }
      resolve(true)
    })
  }

  /**
   * 检查本次更新是否有需要监听的字短
   * @param data 
   */
  async checkWatch(data:KeyValue){
    let keys=Object.keys(data)
    let _this=this;
    this.get(keys).then((res:KeyValue) => {
      for(let i=0;i<keys.length;i++){
        const key=keys[i]
       if(_this.watchKeys.includes(key)){//在监听里面
        //判断是否有变化
        if(JSON.stringify(data[key])!=JSON.stringify(res[key])){//变更
          _this.$runtimeBus.$emitAllTabs(_this.keys.listener,{key,oldVal:data[key],newVal:res[key]})
        }
       }
      }
    }).catch((e:any)=>{
      Promise.reject(e.message||'未知异常')
    })
  }


}
export default StorageDataService