import EventTarget from './EventTarget.js'

const _requestHeader = new WeakMap()
const _responseHeader = new WeakMap()
const _requestTask = new WeakMap()

function _triggerEvent(type, event = {}) {
    event.target = event.target || this

    if (typeof this[`on${type}`] === 'function') {
        this[`on${type}`].call(this, event)
    }
}

function _changeReadyState(readyState, event = {}) {
    this.readyState = readyState

    event.readyState = readyState;

    _triggerEvent.call(this, 'readystatechange', event)
}

function _isRelativePath(url) {
    return !(/^(http|https|ftp|wxfile):\/\/.*/i.test(url));
}

export default class XMLHttpRequest extends EventTarget {

    constructor() {
        super();

        /*
         * TODO 这一批事件应该是在 XMLHttpRequestEventTarget.prototype 上面的
         */
        this.onabort = null
        this.onerror = null
        this.onload = null
        this.onloadstart = null
        this.onprogress = null
        this.ontimeout = null
        this.onloadend = null

        this.onreadystatechange = null
        this.readyState = 0
        this.response = null
        this.responseText = null
        this.responseType = 'text'
        this.dataType = 'string'
        this.responseXML = null
        this.status = 0
        this.statusText = ''
        this.upload = {}
        this.withCredentials = false

        _requestHeader.set(this, {
            'content-type': 'application/x-www-form-urlencoded'
        })
        _responseHeader.set(this, {})
    }

    abort() {
        const myRequestTask = _requestTask.get(this)

        if (myRequestTask) {
            myRequestTask.abort()
        }
    }

    getAllResponseHeaders() {
        const responseHeader = _responseHeader.get(this)

        return Object.keys(responseHeader).map((header) => {
            return `${header}: ${responseHeader[header]}`
        }).join('\n')
    }

    getResponseHeader(header) {
        return _responseHeader.get(this)[header]
    }

    open(method, url /* async, user, password 这几个参数在小程序内不支持*/ ) {
        this._method = method
        this._url = url
        _changeReadyState.call(this, XMLHttpRequest.OPENED)
    }

    overrideMimeType() {}

    send(data = '') {
        if (this.readyState !== XMLHttpRequest.OPENED) {
            throw new Error("Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.")
        } else {
            const url = this._url
            const header = _requestHeader.get(this)
            const responseType = this.responseType
            const dataType = this.dataType

            const relative = _isRelativePath(url)
            let encoding;

            if (responseType === 'arraybuffer') {
                // encoding = 'binary'
            } else {
                encoding = 'utf8'
            }

            delete this.response;
            this.response = null;

            const onSuccess = ({ data, statusCode, header }) => {
                statusCode = statusCode === undefined ? 200 : statusCode;
                if (typeof data !== 'string' && !(data instanceof ArrayBuffer)) {
                    try {
                        data = JSON.stringify(data)
                    } catch (e) {}
                }

                this.status = statusCode
                if (header) {
                    _responseHeader.set(this, header)
                }
                _triggerEvent.call(this, 'loadstart')
                _changeReadyState.call(this, XMLHttpRequest.HEADERS_RECEIVED)
                _changeReadyState.call(this, XMLHttpRequest.LOADING)

                this.response = data

                if (data instanceof ArrayBuffer) {
                    Object.defineProperty(this, 'responseText', {
                        enumerable: true,
                        configurable: true,
                        get: function() {
                            throw "InvalidStateError : responseType is " + this.responseType;
                        }
                    });
                } else {
                    this.responseText = data
                }
                _changeReadyState.call(this, XMLHttpRequest.DONE)
                _triggerEvent.call(this, 'load')
                _triggerEvent.call(this, 'loadend')
            }

            const onFail = ({ errMsg }) => {
                // TODO 规范错误

                if (errMsg.indexOf('abort') !== -1) {
                    _triggerEvent.call(this, 'abort')
                } else {
                    _triggerEvent.call(this, 'error', {
                        message: errMsg
                    })
                }
                _triggerEvent.call(this, 'loadend')

                if (relative) {
                    // 用户即使没监听error事件, 也给出相应的警告
                    console.warn(errMsg)
                }

                // 失败后，重置函数
                this.onabort = null;
                this.onerror = null;
                this.onload = null;
                this.onloadstart = null;
                this.onprogress = null;
                this.ontimeout = null;
                this.onloadend = null;
                this.onreadystatechange = null;
            }

            // 兜底小程序的兼容 adapter
            if (url.indexOf('readHugeFile://') > -1) {
                bl.internal.readHugeFile({
                    filePath: url.replace(/readHugeFile:\/\//, ''),
                    success: (res) => {
                        onSuccess({
                            data: res.data,
                            statusCode: 200
                        })
                    },
                    fail: onFail
                })
                return;
            }

            if (relative) {
                const fs = bl.getFileSystemManager();

                var options = {
                    'filePath': url,
                    'success': onSuccess,
                    'fail': onFail
                }
                if (encoding) {
                    options['encoding'] = encoding;
                }
                fs.readFile(options)
                return
            }

            bl.request({
                data,
                url: url,
                method: this._method,
                header: header,
                dataType: dataType,
                responseType: responseType,
                success: onSuccess,
                fail: onFail
            })
        }
    }

    setRequestHeader(header, value) {
        const myHeader = _requestHeader.get(this)

        myHeader[header] = value
        _requestHeader.set(this, myHeader)
    }

    addEventListener(type, listener) {
        if (typeof listener !== 'function') {
            return;
        }

        this['on' + type] = (event = {}) => {
            event.target = event.target || this
            listener.call(this, event)
        }
    }

    removeEventListener(type, listener) {
        if (this['on' + type] === listener) {
            this['on' + type] = null;
        }
    }
}

// TODO 没法模拟 HEADERS_RECEIVED 和 LOADING 两个状态
XMLHttpRequest.UNSEND = 0
XMLHttpRequest.OPENED = 1
XMLHttpRequest.HEADERS_RECEIVED = 2
XMLHttpRequest.LOADING = 3
XMLHttpRequest.DONE = 4
