package testapp.endpoint;

import org.osgl.$;
import org.osgl.http.H;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;

import java.util.List;
import java.util.Map;

public class EndPointTestContext {

    public enum RequestMethod {
        GET() {
            @Override
            public void verify(EndpointTester tester, EndPointTestContext context) throws Exception {
                tester.setup();
                tester.url(context.url).get(context.params);
                tester.bodyEq(context.expected);
            }
        },
        POST_FORM_DATA() {
            @Override
            public void verify(EndpointTester tester, EndPointTestContext context) throws Exception {
                tester.setup();
                tester.url(context.url).post(context.params);
                tester.bodyEq(context.expected);
            }
        }, POST_JSON () {
            @Override
            public void verify(EndpointTester tester, EndPointTestContext context) throws Exception {
                tester.setup();
                Object payload = context.jsonBody;
                if (null != payload) {
                    tester.url(context.url).postJSON(payload);
                } else {
                    Map<String, Object> params = tester.prepareJsonData(context.params);
                    tester.url(context.url).postJSON(params);
                }
                if (null != context.accept) {
                    tester.reqBuilder.accept(context.accept);
                }
                tester.bodyEq(context.expected, context.expected2, context.expected3);
            }
        };

        public abstract void verify(EndpointTester tester, EndPointTestContext context) throws Exception;
    }

    private String url;
    private String expected;
    // Our JSON deserializer will convert double into BigDecimal, thus
    // the output string will be different, which should be acceptable
    // E.g. double value: "1.7976931348623157E308" should equals to
    //      big decimal value: "1.7976931348623157E+308"
    private String expected2;
    // plus the Set of big decimals
    private String expected3;
    private List<$.T2<String, Object>> params;
    private Object jsonBody;
    private H.Format accept;
    private RequestMethod requestMethod;

    public EndPointTestContext() {}

    public EndPointTestContext url(String url) {
        this.url = $.notNull(url);
        return this;
    }

    public EndPointTestContext expected(String expected) {
        this.expected = expected;
        return this;
    }

    public EndPointTestContext expected(String expected, String expected2, String expected3) {
        this.expected = expected;
        this.expected2 = expected2;
        this.expected3 = expected3;
        return this;
    }

    public EndPointTestContext jsonBody(Object body) {
        this.jsonBody = body;
        return this;
    }

    public EndPointTestContext accept(H.Format format){
        this.accept = format;
        return this;
    }

    public EndPointTestContext params(List<$.T2<String, Object>> params) {
        this.params = params;
        return this;
    }

    public EndPointTestContext params(String key, Object val, Object ... otherPairs) {
        E.illegalArgumentIf(otherPairs.length % 2 != 0);
        this.params = C.newSizedList(1 + otherPairs.length / 2);
        this.params.add($.T2(key, val));
        for (int i = 0; i < otherPairs.length - 1; ++i) {
            this.params.add($.T2(S.string(otherPairs[i]), otherPairs[i+1]));
        }
        return this;
    }

    public EndPointTestContext method(RequestMethod requestMethod) {
        this.requestMethod = requestMethod;
        return this;
    }

    public void applyTo(EndpointTester tester) throws Exception {
        requestMethod.verify(tester, this);
    }
}
