import * as Chai from 'chai';

const expect = Chai.expect;
const fail = Chai.assert.fail;

import middleware from '../lib/index';
import * as mocks from 'node-mocks-http';

import {expacl} from "../expacl";
import ACLRequest = expacl.ACLRequest;

const createRequest = (path: string, user: string | undefined): ACLRequest => {
    const req = mocks.createRequest<ACLRequest>();
    req.user = user;
    req.url = path;
    return req;
};

const createResponse = (validStatuses?: number | number[], done?: MochaDone) => {
    const res = mocks.createResponse();
    if (validStatuses && done) {
        res.status = (code: number) => {
            const contains = Array.isArray(validStatuses) ? (validStatuses.indexOf(code) !== -1) : (validStatuses === code);
            contains ? done() : fail(code, validStatuses, `Expected response code to be ${validStatuses} but instead received ${code}`);
            return res;
        }
    } else {
        res.status = (code: number) => {
            if (code !== 200) {
                fail(code, 200, `Expected response code to be 200 but instead received ${code}`);
            }
            return res;
        }
    }
    return res;
};

describe('Test ExpACL middleware', () => {
    it('Import should return a middleware factory', () => {
        expect(typeof middleware).to.equal("function");
    });

    it('Middleware factory should return an express middleware function', () => {
        const _emw = middleware({
            routes: [
                {
                    path: '/'
                }
            ]
        });
        expect(typeof _emw).to.equal("function");
    });

    describe('Test routes matching', () => {
        const routes = [
            {
                path: '/',
                subroutes: [
                    {
                        path: 'subpath1',
                    },
                    {
                        path: 'subpath2',
                        subroutes: [
                            {
                                path: 'subsubpath2_1',
                            },
                            {
                                path: 'subsubpath2_2',
                                subroutes: [
                                    {
                                        path: 'subsubsubpath2_2_1',
                                    },
                                ],
                            },
                        ],
                    },
                    {
                        path: 'subpath3',
                        transient: true,
                    },
                    {
                        path: 'subpath4',
                        transient: true,
                        subroutes: [
                            {
                                path: 'subsubpath4_1',
                            },
                        ],
                    },
                    {
                        path: 'subpath5',
                        transient: true,
                        subroutes: [
                            {
                                path: '*',
                            },
                        ],
                    },
                    {
                        path: 'subpath6',
                        subroutes: [
                            {
                                path: 'subsubpath6_1',
                            },
                            {
                                path: 'subsubpath6_2',
                                subroutes: [
                                    {
                                        path: '*',
                                    },
                                ],
                            },
                        ],
                    },
                ],
            }
        ];
        const _mw = middleware({missingRoute: 'deny', routes});

        it('route = /: should be accessible', (done) => {
            _mw(createRequest('/', undefined), createResponse(), done);
        });

        it('route = /subpath1: should be accessible', (done) => {
            _mw(createRequest('/subpath1', undefined), createResponse(), done);
        });

        it('route = /subpath1/subsubpath1_1: should return 404', (done) => {
            _mw(createRequest('/subpath1/subsubpath1_1', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath2: should be accessibe', (done) => {
            _mw(createRequest('/subpath2', undefined), createResponse(), done);
        });

        it('route = /subpath2/subsubpath2_1: should be accessible', (done) => {
            _mw(createRequest('/subpath2/subsubpath2_1', undefined), createResponse(), done);
        });

        it('route = /subpath2/subsubpath2_2: should be accessible', (done) => {
            _mw(createRequest('/subpath2/subsubpath2_2', undefined), createResponse(), done);
        });

        it('route = /subpath2/subsubpath2_3: should return 404', (done) => {
            _mw(createRequest('/subpath2/subsubpath2_3', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath2/subsubpath2_2/subsubsubpath2_2_1: should be accessible', (done) => {
            _mw(createRequest('/subpath2/subsubpath2_2', undefined), createResponse(), done);
        });

        it('route = /subpath2/subsubpath2_2/subsubsubpath2_2_2: should return 404', (done) => {
            _mw(createRequest('/subpath2/subsubpath2_2/subsubsubpath2_2_2', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath3: should return 404', (done) => {
            _mw(createRequest('/subpath3', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath4: should return 404', (done) => {
            _mw(createRequest('/subpath4', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath4/subsubpath4_1: should be accessible', (done) => {
            _mw(createRequest('/subpath4/subsubpath4_1', undefined), createResponse(), done);
        });

        it('route = /subpath5: should return 404', (done) => {
            _mw(createRequest('/subpath5', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath5/subsubpath5_1: should be accessible', (done) => {
            _mw(createRequest('/subpath5/subsubpath5_1', undefined), createResponse(), done);
        });

        it('route = /subpath5/subsubpath5_1/subsubsubpath5_1_1: should be accessible', (done) => {
            _mw(createRequest('/subpath5/subsubpath5_1/subsubsubpath5_1_1', undefined), createResponse(), done);
        });

        it('route = /subpath6: should be accessible', (done) => {
            _mw(createRequest('/subpath6', undefined), createResponse(), done);
        });

        it('route = /subpath6/subsubpath6_1: should be accessible', (done) => {
            _mw(createRequest('/subpath6/subsubpath6_1', undefined), createResponse(), done);
        });

        it('route = /subpath6/subsubpath6_2: should be accessible', (done) => {
            _mw(createRequest('/subpath6/subsubpath6_2', undefined), createResponse(), done);
        });

        it('route = /subpath6/subsubpath6_3: should return 404', (done) => {
            _mw(createRequest('/subpath6/subsubpath6_3', undefined), createResponse([404], done), fail);
        });

        it('route = /subpath6/subsubpath6_2/subsubsubpath6_2_1: should be accessible', (done) => {
            _mw(createRequest('/subpath6/subsubpath6_2/subsubsubpath6_2_1', undefined), createResponse(), done);
        });
    });

    describe('Test missingRoute option ', () => {
        const routes = [
            {
                path: '/'
            }
        ];

        const _allow = middleware({missingRoute: 'allow', routes});
        const _deny = middleware({missingRoute: 'deny', routes});

        it('missingRoute = allow: Should allow access to existing paths', (done) => {
            _allow(createRequest('/', undefined), createResponse(), done);
        });

        it('missingRoute = allow: Should allow access to missing paths', (done) => {
            _allow(createRequest('/missing', undefined), createResponse(), done);
        });

        it('missingRoute = deny: Should allow access to existing paths', (done) => {
            _deny(createRequest('/', undefined), createResponse(), done);
        });

        it('missingRoute = deny: Should deny access to missing paths', (done) => {
            _deny(createRequest('/missing', undefined), createResponse([404], done), fail);
        });
    })

});