package zmo.technology.lite.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import com.alibaba.fastjson.JSONObject;

public abstract class ZeroController {

    protected static final String GENERAL_HANDLER_PATH = "/**";

    private String prefix;
    private Map<String, Method> handlers;

    protected ZeroController(String prefix) {
        super();
        if (!prefix.startsWith("/"))
            this.prefix = "/" + prefix;
        else
            this.prefix = prefix;
    }

    private Map<String, Method> getHandlers() {
        if (handlers == null) {
            handlers = new ConcurrentHashMap<>();
            Method[] methods = getClass().getMethods();
            for (Method method : methods) {
                if (method.getParameters().length == 1
                        && method.getParameters()[0].getType().equals(ZeroRequestGuider.class)) {
                    handlers.put(method.getName(), method);
                }
            }
        }
        return handlers;
    }

    protected void proxyHandler(DataSource dataSource, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        String[] items = request.getRequestURI().substring(request.getRequestURI().indexOf(prefix) + prefix.length() + 1).split("/");
        List<String> paths = new ArrayList<>();
        for (int i = 0; i < items.length; i++)
            if (!items[i].trim().isEmpty())
                paths.add(items[i].trim());

        ZeroRequestGuider guider = ZeroRequestGuider.build(request, response, paths);
        guider.responseJSONHeader();

        String handlerName = "";
        try {
            handlerName = handlerNameConstructor(guider);
        } catch (Exception e) {
            e.printStackTrace();
            Throwable throwable = e;
            while (throwable.getCause() != null)
                throwable = throwable.getCause();
            if (throwable instanceof ZeroException) {
                ZeroException exception = (ZeroException) throwable;
                guider.write(ZeroResponse.build().setCode(exception.getCode())
                        .setMessage(exception.getMessage()));
            } else
                guider.write(ZeroResponse.build().setCode(500).setMessage(throwable.toString()));
            return;
        }

        if (!getHandlers().containsKey(handlerName)) {
            guider.write(ZeroResponse.build().setCode(404).setMessage("handler not found"));
        } else {
            Connection connection = dataSource.getConnection();
            try {
                connection.setAutoCommit(false);
                guider.setConnection(connection);
                Method method = getHandlers().get(handlerName);
                before(guider);
                method.invoke(this, guider);
                after(guider);
                connection.commit();
            } catch (Exception e) {
                e.printStackTrace();
                connection.rollback();
                Throwable throwable = e;
                while (throwable.getCause() != null)
                    throwable = throwable.getCause();
                if (throwable instanceof ZeroException) {
                    ZeroException exception = (ZeroException) throwable;
                    guider.write(ZeroResponse.build().setCode(exception.getCode())
                            .setMessage(exception.getMessage()));
                } else
                    guider.write(ZeroResponse.build().setCode(500).setMessage(throwable.toString()));
            } finally {
                connection.close();
            }
        }
    }

    protected ZeroResponse<?> success() {
        return ZeroResponse.build().setCode(200).setMessage("success");
    }

    protected <T> ZeroResponse<T> success(List<T> datas) {
        return ZeroResponse.build(datas).setCode(200).setMessage("success");
    }

    protected <T> ZeroResponse<T> success(List<T> datas, JSONObject expands) {
        return ZeroResponse.build(datas).setExpands(expands).setCode(200).setMessage("success");
    }

    protected abstract String handlerNameConstructor(ZeroRequestGuider guider) throws Exception;

    protected void before(ZeroRequestGuider guider) throws Exception {

    }

    protected void after(ZeroRequestGuider guider) throws Exception {

    }
}
