module vweb

struct RoutePair {
	url   string
	route string
}

fn (rp RoutePair) test() ?[]string {
	url := rp.url.split('/').filter(it != '')
	route := rp.route.split('/').filter(it != '')
	return route_matches(url, route)
}

fn (rp RoutePair) test_match() {
	rp.test() or { panic('should match: $rp') }
}

fn (rp RoutePair) test_no_match() {
	rp.test() or { return }
	panic('should not match: $rp')
}

fn (rp RoutePair) test_param(expected []string) {
	res := rp.test() or { panic('should match: $rp') }
	assert res == expected
}

fn test_route_no_match() {
	tests := [
		RoutePair{
			url: '/a'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/b'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/b/'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/c/b'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/c/b/'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/b/c/d'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/b/c'
			route: '/'
		},
	]
	for test in tests {
		test.test_no_match()
	}
}

fn test_route_exact_match() {
	tests := [
		RoutePair{
			url: '/a/b/c'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a/b/c/'
			route: '/a/b/c'
		},
		RoutePair{
			url: '/a'
			route: '/a'
		},
		RoutePair{
			url: '/'
			route: '/'
		},
	]
	for test in tests {
		test.test_match()
	}
}

fn test_route_params_match() {
	RoutePair{
		url: '/a/b/c'
		route: '/:a/b/c'
	}.test_match()

	RoutePair{
		url: '/a/b/c'
		route: '/a/:b/c'
	}.test_match()

	RoutePair{
		url: '/a/b/c'
		route: '/a/b/:c'
	}.test_match()

	RoutePair{
		url: '/a/b/c'
		route: '/:a/b/:c'
	}.test_match()

	RoutePair{
		url: '/a/b/c'
		route: '/:a/:b/:c'
	}.test_match()

	RoutePair{
		url: '/one/two/three'
		route: '/:a/:b/:c'
	}.test_match()

	RoutePair{
		url: '/one/b/c'
		route: '/:a/b/c'
	}.test_match()

	RoutePair{
		url: '/one/two/three'
		route: '/:a/b/c'
	}.test_no_match()

	RoutePair{
		url: '/one/two/three'
		route: '/:a/:b/c'
	}.test_no_match()

	RoutePair{
		url: '/one/two/three'
		route: '/:a/b/:c'
	}.test_no_match()

	RoutePair{
		url: '/a/b/c/d'
		route: '/:a/:b/:c'
	}.test_no_match()

	RoutePair{
		url: '/1/2/3/4'
		route: '/:a/:b/:c'
	}.test_no_match()

	RoutePair{
		url: '/a/b'
		route: '/:a/:b/:c'
	}.test_no_match()

	RoutePair{
		url: '/1/2'
		route: '/:a/:b/:c'
	}.test_no_match()
}

fn test_route_params() {
	RoutePair{
		url: '/a/b/c'
		route: '/:a/b/c'
	}.test_param(['a'])

	RoutePair{
		url: '/one/b/c'
		route: '/:a/b/c'
	}.test_param(['one'])

	RoutePair{
		url: '/one/two/c'
		route: '/:a/:b/c'
	}.test_param(['one', 'two'])

	RoutePair{
		url: '/one/two/three'
		route: '/:a/:b/:c'
	}.test_param(['one', 'two', 'three'])

	RoutePair{
		url: '/one/b/three'
		route: '/:a/b/:c'
	}.test_param(['one', 'three'])
}

fn test_route_params_array_match() {
	// array can only be used on the last word (TODO: add parsing / tests to ensure this)

	RoutePair{
		url: '/a/b/c'
		route: '/a/b/:c...'
	}.test_match()

	RoutePair{
		url: '/a/b/c/d'
		route: '/a/b/:c...'
	}.test_match()

	RoutePair{
		url: '/a/b/c/d/e'
		route: '/a/b/:c...'
	}.test_match()

	RoutePair{
		url: '/one/b/c/d/e'
		route: '/:a/b/:c...'
	}.test_match()

	RoutePair{
		url: '/one/two/c/d/e'
		route: '/:a/:b/:c...'
	}.test_match()

	RoutePair{
		url: '/one/two/three/four/five'
		route: '/:a/:b/:c...'
	}.test_match()

	RoutePair{
		url: '/a/b'
		route: '/:a/:b/:c...'
	}.test_no_match()

	RoutePair{
		url: '/a/b/'
		route: '/:a/:b/:c...'
	}.test_no_match()
}

fn test_route_params_array() {
	RoutePair{
		url: '/a/b/c'
		route: '/a/b/:c...'
	}.test_param(['c'])

	RoutePair{
		url: '/a/b/c/d'
		route: '/a/b/:c...'
	}.test_param(['c/d'])

	RoutePair{
		url: '/a/b/c/d/'
		route: '/a/b/:c...'
	}.test_param(['c/d'])

	RoutePair{
		url: '/a/b/c/d/e'
		route: '/a/b/:c...'
	}.test_param(['c/d/e'])

	RoutePair{
		url: '/one/b/c/d/e'
		route: '/:a/b/:c...'
	}.test_param(['one', 'c/d/e'])

	RoutePair{
		url: '/one/two/c/d/e'
		route: '/:a/:b/:c...'
	}.test_param(['one', 'two', 'c/d/e'])

	RoutePair{
		url: '/one/two/three/d/e'
		route: '/:a/:b/:c...'
	}.test_param(['one', 'two', 'three/d/e'])
}

fn test_route_index_path() {
	RoutePair{
		url: '/'
		route: '/:path...'
	}.test_param(['/'])

	RoutePair{
		url: '/foo/bar'
		route: '/:path...'
	}.test_param(['/foo/bar'])
}