const request = require('request')
class Plex {
    constructor(opts) {
        this._accessToken = typeof opts.accessToken !== 'undefined' ? opts.accessToken : ''
        let uri = "http://127.0.0.1:32400";
        if ( (typeof opts.uri) !== 'undefined' ) {
            uri = opts.uri;
            if (uri.endsWith("/")) {
                uri = uri.slice(0, uri.length - 1);
            }
        }
        this._server = {
            uri: uri,
            host: typeof opts.host !== 'undefined' ? opts.host : '127.0.0.1',
            port: typeof opts.port !== 'undefined' ? opts.port : '32400',
            protocol: typeof opts.protocol !== 'undefined' ? opts.protocol : 'http'
        }
        this._headers = {
            'Accept': 'application/json',
            'X-Plex-Device': 'dizqueTV',
            'X-Plex-Device-Name': 'dizqueTV',
            'X-Plex-Product': 'dizqueTV',
            'X-Plex-Version': '0.1',
            'X-Plex-Client-Identifier': 'rg14zekk3pa5zp4safjwaa8z',
            'X-Plex-Platform': 'Chrome',
            'X-Plex-Platform-Version': '80.0'
        }
    }

    get URL() { return `${this._server.uri}` }

    SignIn(username, password) {
        return new Promise((resolve, reject) => {
            if (typeof username === 'undefined' || typeof password === 'undefined')
                reject("Plex 'SignIn' Error - No Username or Password was provided to sign in.")
            var req = {
                method: 'post',
                url: 'https://plex.tv/users/sign_in.json',
                headers: this._headers,
                form: {
                    user: {
                        login: username,
                        password: password
                    }
                },
                jar: false
            }
            request(req, (err, res, body) => {
                if (err || res.statusCode !== 201)
                    reject("Plex 'SignIn' Error - Username/Email and Password is incorrect!.")
                else {
                    this._accessToken = JSON.parse(body).user.authToken
                    resolve({ accessToken: this._accessToken })
                }
            })
        })
    }

    doRequest(req) {
        return new Promise( (resolve, reject) => {
            request( req, (err, res) => {
                if (err) {
                    reject(err);
                } else if ((res.statusCode < 200) || (res.statusCode >= 300) ) {
                    reject( Error(`Request returned status code ${res.statusCode}`) );
                } else {
                    resolve(res);
                }
            });
        });
    }

    async Get(path, optionalHeaders = {}) {
        let req = {
            method: 'get',
            url: `${this.URL}${path}`,
            headers: this._headers,
            jar: false
        }
        Object.assign(req, optionalHeaders)
        req.headers['X-Plex-Token'] = this._accessToken
        if (this._accessToken === '') {
            throw Error("No Plex token provided. Please use the SignIn method or provide a X-Plex-Token in the Plex constructor.");
        } else {
            let res = await this.doRequest(req);
            return JSON.parse(res.body).MediaContainer;
        }
    }
    async Put(path, query = {}, optionalHeaders = {}) {
        var req = {
            method: 'put',
            url: `${this.URL}${path}`,
            headers: this._headers,
            qs: query,
            jar: false
        }
        Object.assign(req, optionalHeaders)
        req.headers['X-Plex-Token'] = this._accessToken
        await new Promise((resolve, reject) => {
            if (this._accessToken === '')
                reject("No Plex token provided. Please use the SignIn method or provide a X-Plex-Token in the Plex constructor.")
            else
                request(req, (err, res) => {
                    if (err || res.statusCode !== 200)
                        reject(`Plex 'Put' request failed. URL: ${this.URL}${path}`)
                    else
                        resolve(res.body)
                })
        })
    }
    Post(path, query = {}, optionalHeaders = {}) {
        var req = {
            method: 'post',
            url: `${this.URL}${path}`,
            headers: this._headers,
            qs: query,
            jar: false
        }
        Object.assign(req, optionalHeaders)
        req.headers['X-Plex-Token'] = this._accessToken
        return new Promise((resolve, reject) => {
            if (this._accessToken === '')
                reject("No Plex token provided. Please use the SignIn method or provide a X-Plex-Token in the Plex constructor.")
            else
                request(req, (err, res) => {
                    if (err || res.statusCode !== 200)
                        reject(`Plex 'Post' request failed. URL: ${this.URL}${path}`)
                    else
                        resolve(res.body)
                })
        })
    }
    async checkServerStatus() {
        try {
            await this.Get('/');
            return 1;
        } catch (err) {
            console.error("Error getting Plex server status", err);
            return -1;
        }
    }
    async GetDVRS() {
        try {
            var result = await this.Get('/livetv/dvrs')
            var dvrs = result.Dvr
            dvrs = typeof dvrs === 'undefined' ? [] : dvrs
            return dvrs
        } catch (err) {
            throw Error( "GET /livetv/drs failed: " + err.message);
        }
    }
    async RefreshGuide(_dvrs) {
        try {
            var dvrs = typeof _dvrs !== 'undefined' ? _dvrs : await this.GetDVRS()
            for (var i = 0; i < dvrs.length; i++) {
                await this.Post(`/livetv/dvrs/${dvrs[i].key}/reloadGuide`);
            }
        } catch (err) {
            throw Error("Zort", err);
        }
    }
    async RefreshChannels(channels, _dvrs) {
        var dvrs = typeof _dvrs !== 'undefined' ? _dvrs : await this.GetDVRS()
        var _channels = []
        let qs = {}
        for (var i = 0; i < channels.length; i++) {
            _channels.push(channels[i].number)
        }
        qs.channelsEnabled = _channels.join(',')
        for (var i = 0; i < _channels.length; i++) {
            qs[`channelMapping[${_channels[i]}]`] = _channels[i]
            qs[`channelMappingByKey[${_channels[i]}]`] = _channels[i]
        }
        for (var i = 0; i < dvrs.length; i++) {
            for (var y = 0; y < dvrs[i].Device.length; y++) {
                await this.Put(`/media/grabbers/devices/${dvrs[i].Device[y].key}/channelmap`, qs);
            }
        }
    }
}

module.exports = Plex
