const errors = require('@tryghost/errors');
const should = require('should');
const sinon = require('sinon');
const shared = require('../../../');

describe('validators/input/all', function () {
    afterEach(function () {
        sinon.restore();
    });

    describe('all', function () {
        it('default', function () {
            const frame = {
                options: {
                    context: {},
                    slug: 'slug',
                    include: 'tags,authors',
                    page: 2
                }
            };

            const apiConfig = {
                options: {
                    include: {
                        values: ['tags', 'authors'],
                        required: true
                    }
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(() => {
                    should.exist(frame.options.page);
                    should.exist(frame.options.slug);
                    should.exist(frame.options.include);
                    should.exist(frame.options.context);
                });
        });

        it('should run global validations on an type that has validation defined', function () {
            const frame = {
                options: {
                    slug: 'not a valid slug %%%%% http://'
                }
            };

            const apiConfig = {
                options: {
                    slug: {
                        required: true
                    }
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(() => {
                    throw new Error('Should not resolve');
                }, (err) => {
                    should.exist(err);
                });
        });

        it('allows empty values', function () {
            const frame = {
                options: {
                    context: {},
                    formats: ''
                }
            };

            const apiConfig = {
                options: {
                    formats: ['format1']
                }
            };

            return shared.validators.input.all.all(apiConfig, frame);
        });

        it('supports include being an array', function () {
            const frame = {
                options: {
                    context: {},
                    slug: 'slug',
                    include: ['tags', 'authors'],
                    page: 2
                }
            };

            const apiConfig = {
                options: {
                    include: {
                        values: ['tags', 'authors'],
                        required: true
                    }
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(() => {
                    should.exist(frame.options.page);
                    should.exist(frame.options.slug);
                    should.exist(frame.options.include);
                    should.exist(frame.options.context);
                });
        });

        it('default include array notation', function () {
            const frame = {
                options: {
                    context: {},
                    slug: 'slug',
                    include: 'tags,authors',
                    page: 2
                }
            };

            const apiConfig = {
                options: {
                    include: ['tags', 'authors']
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(() => {
                    should.exist(frame.options.page);
                    should.exist(frame.options.slug);
                    should.exist(frame.options.include);
                    should.exist(frame.options.context);
                });
        });

        it('does not fail', function () {
            const frame = {
                options: {
                    context: {},
                    include: 'tags,authors'
                }
            };

            const apiConfig = {
                options: {
                    include: {
                        values: ['tags']
                    }
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(Promise.reject.bind(Promise))
                .catch((err) => {
                    should.not.exist(err);
                });
        });

        it('does not fail include array notation', function () {
            const frame = {
                options: {
                    context: {},
                    include: 'tags,authors'
                }
            };

            const apiConfig = {
                options: {
                    include: ['tags']
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(Promise.reject.bind(Promise))
                .catch((err) => {
                    should.not.exist(err);
                });
        });

        it('fails', function () {
            const frame = {
                options: {
                    context: {}
                }
            };

            const apiConfig = {
                options: {
                    include: {
                        required: true
                    }
                }
            };

            return shared.validators.input.all.all(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                });
        });

        it('invalid fields', function () {
            const frame = {
                options: {
                    context: {},
                    id: 'invalid'
                }
            };

            const apiConfig = {};

            return shared.validators.input.all.all(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                });
        });
    });

    describe('browse', function () {
        it('default', function () {
            const frame = {
                options: {
                    context: {}
                },
                data: {
                    status: 'aus'
                }
            };

            const apiConfig = {};

            shared.validators.input.all.browse(apiConfig, frame);
            should.exist(frame.options.context);
            should.exist(frame.data.status);
        });

        it('fails', function () {
            const frame = {
                options: {
                    context: {}
                },
                data: {
                    id: 'no-id'
                }
            };

            const apiConfig = {};

            return shared.validators.input.all.browse(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                });
        });
    });

    describe('read', function () {
        it('default', function () {
            sinon.stub(shared.validators.input.all, 'browse');

            const frame = {
                options: {
                    context: {}
                }
            };

            const apiConfig = {};

            shared.validators.input.all.read(apiConfig, frame);
            shared.validators.input.all.browse.calledOnce.should.be.true();
        });
    });

    describe('add', function () {
        it('fails', function () {
            const frame = {
                data: {}
            };

            const apiConfig = {
                docName: 'docName'
            };

            return shared.validators.input.all.add(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                });
        });

        it('fails with docName', function () {
            const frame = {
                data: {
                    docName: true
                }
            };

            const apiConfig = {
                docName: 'docName'
            };

            return shared.validators.input.all.add(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                });
        });

        it('fails for required field', function () {
            const frame = {
                data: {
                    docName: [{
                        a: 'b'
                    }]
                }
            };

            const apiConfig = {
                docName: 'docName',
                data: {
                    b: {
                        required: true
                    }
                }
            };

            return shared.validators.input.all.add(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                    err.message.should.eql('Validation (FieldIsRequired) failed for ["b"]');
                });
        });

        it('fails for invalid field', function () {
            const frame = {
                data: {
                    docName: [{
                        a: 'b',
                        b: null
                    }]
                }
            };

            const apiConfig = {
                docName: 'docName',
                data: {
                    b: {
                        required: true
                    }
                }
            };

            return shared.validators.input.all.add(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    should.exist(err);
                    err.message.should.eql('Validation (FieldIsInvalid) failed for ["b"]');
                });
        });

        it('success', function () {
            const frame = {
                data: {
                    docName: [{
                        a: 'b'
                    }]
                }
            };

            const apiConfig = {
                docName: 'docName'
            };

            const result = shared.validators.input.all.add(apiConfig, frame);
            (result instanceof Promise).should.not.be.true();
        });
    });

    describe('edit', function () {
        it('id mismatch', function () {
            const apiConfig = {
                docName: 'users'
            };

            const frame = {
                options: {
                    id: 'zwei'
                },
                data: {
                    posts: [
                        {
                            id: 'eins'
                        }
                    ]
                }
            };

            return shared.validators.input.all.edit(apiConfig, frame)
                .then(Promise.reject)
                .catch((err) => {
                    (err instanceof errors.BadRequestError).should.be.true();
                });
        });
    });
});
