const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if (this.state === PENDING) {
                this.state = RESOLVE;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if (this.state === PENDING) {
                this.state = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }

    then(onFulfilled, onRejected) {
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data;

      onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

      let promise2 = new Promise((resolve, reject) => {
        if (this.state === RESOLVE) {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              // x 可能是普通纸 也可能是 promise
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          },0)
        }
        if (this.state === REJECTED) {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              // x 可能是普通纸 也可能是 promise
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) {
              reject(e)
            }
          },0)
        }
        if (this.state === PENDING) {
          this.onResolvedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                // x 可能是普通纸 也可能是 promise
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e)
              }
            },0)
          })
          this.onRejectedCallbacks.push(() => {
            setTimeout(() => {
              try {
                let x = onRejected(this.reason);
                // x 可能是普通纸 也可能是 promise
                resolvePromise(promise2, x, resolve, reject);
              } catch(e) {
                reject(e)
              }
            },0)
          })
        }
      })
      return promise2;
    }

    catch(fn) {
      return this.then(null, fn);
    }
}

// resolve 方法
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}

// reject 方法
Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}

// race 方法 
Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
    for(let i=0;i < promises.length;i++){
      promises[i].then(resolve,reject)
    };
  })
}

// all 方法 
Promise.all = function(values) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let index = 0;
    function processData(key, value) {
      arr[key] = value;
      if(index++ == values.length) {
        resolve(arr);
      }
    }
    for(let i = 0; i < values.length; i++) {
      let current = values[i];
      if (isPromise(current)) {
        current.then((data) => {
          processData(i, data);
        }, reject);
      }else {
        processData(i, current);
      }
    }
  })
}

// finally 方法 
Promise.prototype.finally = function(cb) {
  return this.then(data => {
    return Promise.resolve(cb()).then(() => data);
  }, err => {
    Promise.resolve(cb()).then(() => {
      throw err;
    });
  })
}

const isPromise = (value) => {
  if(typeof value === 'object' && value !== null || typeof value === 'function') {
    if (typeof value.then === 'function') {
      return true;
    }else {
      return false;
    }
  }
}

resolvePromise = (promise2,x,resolve,reject) => {
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
  }
  if(typeof x === 'object' && x !== null || typeof x === 'function') {
    let called; // 内部测试
    try {
      let then = x.then;
      if(typeof then === 'function') {
        then.call(x, y => { // y 可能还是 promise 递归
          if(called) {
            return;
          }
          called = true;
          resolvePromise(promise2, y, resolve, reject); // 采用 promise 成功结果的值向下传递
        },r => {
          if(called) {
            return;
          }
          called = true;
          reject(r);// 采用失败结果向下传递
        })
      } else {
        resolve(x); // 说明x 是一个普通的对象 直接成功
      }
    } catch(e) {
      if(called) {
        return;
      }
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

module.exports = Promise;