"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = require("chalk");
const lodash_1 = require("lodash");
const util_1 = require("util");
function isHttpableError(err) {
    return err instanceof Error && typeof err.statusCode === 'function';
}
exports.isHttpableError = isHttpableError;
function isHumanError(err) {
    return err instanceof Error && typeof err.getHumanMessage === 'function';
}
exports.isHumanError = isHumanError;
/**
 * OAuthError is the error type thrown when anything goes wrong in shortcode oauth.
 */
class ShortCodeError extends Error {
}
exports.ShortCodeError = ShortCodeError;
/**
 * A subprocess error is thrown when a subprocess exits with an unexpected
 * status code.
 */
class SubprocessError extends Error {
}
exports.SubprocessError = SubprocessError;
/**
 * UnexpectedHttpError is raised when we get an unexpected status code
 * from Mixer.
 */
class UnexpectedHttpError extends Error {
    constructor(res, text) {
        super(`Unexpected status code ${res.status} ${res.statusText} from ${res.url}: ${text}`);
        this.res = res;
        this.text = text;
    }
    getHumanMessage() {
        const json = this.tryJson();
        return [
            `Unexpected ${this.res.status} ${this.res.statusText} from ${this.res.url}:`,
            '',
            util_1.inspect(json || this.text, false, null, true),
            '',
            chalk_1.default.dim(this.stack.split('\n')
                .slice(1)
                .join('\n')),
        ].join('\n');
    }
    /**
     * For 400 errors, this returns the Joi validation that failed.
     * Returns undefined if it's not a 400 error or for any reason we can't
     * figure out the error message.
     */
    getValidationError() {
        if (this.res.status !== 400) {
            return undefined;
        }
        return lodash_1.get(this.tryJson(), 'details.0', undefined);
    }
    /**
     * Tries to parse the response body as JSON, returning undefined if it can't.
     */
    tryJson() {
        try {
            return JSON.parse(this.text);
        }
        catch (e) {
            return undefined;
        }
    }
}
exports.UnexpectedHttpError = UnexpectedHttpError;
/**
 * ShortCodeExpireError is raised from waitForAccept() if the shortcode expires
 * before the user accepts it. Callers should handle this error.
 */
class ShortCodeExpireError extends ShortCodeError {
    constructor() {
        super('Shortcode handle has expired');
    }
}
exports.ShortCodeExpireError = ShortCodeExpireError;
/**
 * Exception raised when the user denies access to the client in shortcode OAuth.
 */
class ShortCodeAccessDeniedError extends ShortCodeError {
    constructor() {
        super('User has denied access');
    }
}
exports.ShortCodeAccessDeniedError = ShortCodeAccessDeniedError;
/**
 * MixerPluginError is thrown when there's an error in the webpack MixerPlugin.
 */
class MixerPluginError extends Error {
}
exports.MixerPluginError = MixerPluginError;
/**
 * A PackageIntegrityError is thrown during the bundling process if something
 * is amiss in the build process.
 */
class PackageIntegrityError extends Error {
    getHumanMessage() {
        return this.message;
    }
}
exports.PackageIntegrityError = PackageIntegrityError;
/**
 * A WebpackBundlerError is thrown during the bundling process if
 * webpack throws an error.
 */
class WebpackBundlerError extends Error {
}
exports.WebpackBundlerError = WebpackBundlerError;
/**
 * PublishError is thrown during the Publisher's publish or unpublished
 * methods if a bad response code is thrown.
 */
class PublishHttpError extends UnexpectedHttpError {
    getHumanMessage() {
        if (this.res.status === 403) {
            return `You don't own that Interactive bundle!`;
        }
        const invalid = this.getValidationError();
        if (!invalid) {
            return this.stack || this.message; // *shrugs*
        }
        // Rewrite some common errors with more helpful messages, when we can.
        switch (invalid.type) {
            case 'immutableVersion':
                return (`That Interactive version is already published, you cannot modify ` +
                    `it. If you need to unpublish it, run \`miix unpublish\`.`);
            case 'versionAlreadyPublic':
                return (`You already published that version! Maybe you want to update the ` +
                    `version number in your package.json?`);
            case 'string.semver':
                return (`The version number in your package.json must be a semantic version ` +
                    `number, like "1.2.3".`);
            case 'string.regex.base':
                switch (invalid.path) {
                    case 'id':
                        return 'Your package name can only contain numbers, letters, and dashes.';
                    default:
                }
                break;
            default:
        }
        return super.getHumanMessage();
    }
}
exports.PublishHttpError = PublishHttpError;
/**
 * PublishPrivateError is thrown when attempts to run miix publish on
 * a private module.
 */
class PublishPrivateError extends Error {
    getHumanMessage() {
        return (`Your package.json has \`"private": true\` in it, which prevents publishing. ` +
            `Remove this line if you're ready to publish your project to the world!`);
    }
}
exports.PublishPrivateError = PublishPrivateError;
/**
 * UploaderHttpError is thrown by the Updater process.
 */
class UploaderHttpError extends PublishHttpError {
    getHumanMessage() {
        // Hapi seems to be a little weird about this, it *should* properly
        // return a 413 but on my box it gives this error instead.
        if (this.res.status === 400 &&
            lodash_1.get(this.tryJson(), 'message') === 'Invalid multipart payload format') {
            return (`Your bundle is too big to upload! Try cutting your bundle size ` +
                `down, or contact mixer.com/contact for help.`);
        }
        return super.getHumanMessage();
    }
}
exports.UploaderHttpError = UploaderHttpError;
/**
 * BundleNameTakenError is thrown when the user tries to upload a
 * control bundle whose name is already in use.
 */
class BundleNameTakenError extends UploaderHttpError {
    getHumanMessage() {
        return `That bundle name is already taken, shucks!`;
    }
    /**
     * @override
     */
    statusCode() {
        return 400;
    }
}
exports.BundleNameTakenError = BundleNameTakenError;
/**
 * NotInteractiveError is thrown from the connect-participant call on
 * the dev server if the integration isn't interactive yet.
 */
class NotInteractiveError extends Error {
    constructor() {
        super('The requested channel is not interactive yet.');
    }
    /**
     * @override
     */
    statusCode() {
        return 409;
    }
}
exports.NotInteractiveError = NotInteractiveError;
/**
 * NoAuthenticationError is thrown when attempting to execute an action
 * which requires authentication which is not present.
 */
class NoAuthenticationError extends Error {
    constructor() {
        super('You must log in to do that.');
    }
    /**
     * @override
     */
    getHumanMessage() {
        return 'You must be logged in to do that, run `miix login`.';
    }
    /**
     * @override
     */
    statusCode() {
        return 401;
    }
}
exports.NoAuthenticationError = NoAuthenticationError;
/**
 * SaveNotFoundError
 */
class SaveNotFoundError extends Error {
    constructor() {
        super('That save file was not found');
    }
    /**
     * @override
     */
    getHumanMessage() {
        return 'That save file was not found';
    }
    /**
     * @override
     */
    statusCode() {
        return 404;
    }
}
exports.SaveNotFoundError = SaveNotFoundError;
