//
//  ========================================================================
//  Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.rewrite.handler;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;

import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class RewriteRegexRuleTest extends AbstractRuleTestCase
{
    public static Stream<Arguments> scenarios()
    {
        return Stream.of(
                new Scenario("/foo0/bar",null,".*","/replace","/replace",null),
                new Scenario("/foo1/bar","n=v",".*","/replace","/replace","n=v"),
                new Scenario("/foo2/bar",null,"/xxx.*","/replace",null,null),
                new Scenario("/foo3/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo3/xxx",null),
                new Scenario("/f%20o3/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/f%20o3/xxx",null),
                new Scenario("/foo4/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo4"),
                new Scenario("/foo5/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo5"),
                new Scenario("/foo6/bar",null,"/(.*)/(.*)","/foo6/bar?p2=$2&p1=$1","/foo6/bar","p2=bar&p1=foo6"),
                new Scenario("/foo7/bar","n=v","/(.*)/(.*)","/foo7/bar?p2=$2&p1=$1","/foo7/bar","n=v&p2=bar&p1=foo7"),
                new Scenario("/foo8/bar",null,"/(foo8)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo8/xxx",null),
                new Scenario("/foo9/$bar",null,".*","/$replace","/$replace",null),
                new Scenario("/fooA/$bar",null,"/fooA/(.*)","/$1/replace","/$bar/replace",null),
                new Scenario("/fooB/bar/info",null,"/fooB/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"),
                new Scenario("/fooC/bar/info",null,"/fooC/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"),
                new Scenario("/fooD/bar/info","n=v","/fooD/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"),
                new Scenario("/fooE/bar/info","n=v","/fooE/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&p1=bar")
        ).map(Arguments::of);
    }

    @ParameterizedTest
    @MethodSource("scenarios")
    public void testRequestUriEnabled(Scenario scenario) throws Exception
    {
        start(false);
        RewriteRegexRule rule = new RewriteRegexRule();

        reset();
        _request.setURIPathQuery(null);

        rule.setRegex(scenario.regex);
        rule.setReplacement(scenario.replacement);

        _request.setURIPathQuery(scenario.uriPathQuery +(scenario.queryString ==null?"":("?"+scenario.queryString)));

        String result = rule.matchAndApply(scenario.uriPathQuery, _request, _response);
        assertEquals(scenario.expectedRequestURI, result);
        rule.applyURI(_request,scenario.uriPathQuery,result);

        if (result!=null)
        {
            assertEquals(scenario.expectedRequestURI, _request.getRequestURI());
            assertEquals(scenario.expectedQueryString, _request.getQueryString());
        }

        if (scenario.expectedQueryString !=null)
        {
            MultiMap<String> params=new MultiMap<String>();
            UrlEncoded.decodeTo(scenario.expectedQueryString,params, StandardCharsets.UTF_8);

            for (String n:params.keySet())
                assertEquals(params.getString(n),_request.getParameter(n));
        }
    }

    @ParameterizedTest
    @MethodSource("scenarios")
    public void testContainedRequestUriEnabled(Scenario scenario) throws Exception
    {
        start(false);
        RewriteRegexRule rule = new RewriteRegexRule();

        RuleContainer container = new RuleContainer();
        container.setRewriteRequestURI(true);
        container.addRule(rule);

        reset();
        rule.setRegex(scenario.regex);
        rule.setReplacement(scenario.replacement);

        _request.setURIPathQuery(scenario.uriPathQuery);
        _request.setQueryString(scenario.queryString);
        _request.getAttributes().clearAttributes();

        String result = container.apply(URIUtil.decodePath(scenario.uriPathQuery),_request,_response);
        assertEquals(URIUtil.decodePath(scenario.expectedRequestURI ==null?scenario.uriPathQuery :scenario.expectedRequestURI), result);
        assertEquals(scenario.expectedRequestURI ==null?scenario.uriPathQuery :scenario.expectedRequestURI, _request.getRequestURI());
        assertEquals(scenario.expectedQueryString, _request.getQueryString());

    }

    private static class Scenario
    {
        String uriPathQuery;
        String queryString;
        String regex;
        String replacement;
        String expectedRequestURI;
        String expectedQueryString;

        public Scenario(String uriPathQuery, String queryString, String regex, String replacement, String expectedRequestURI, String expectedQueryString)
        {
            this.uriPathQuery = uriPathQuery;
            this.queryString = queryString;
            this.regex = regex;
            this.replacement = replacement;
            this.expectedRequestURI = expectedRequestURI;
            this.expectedQueryString = expectedQueryString;
        }

        @Override
        public String toString()
        {
            return String.format("%s?%s>%s|%s", uriPathQuery, queryString, regex, replacement);
        }
    }
}
