package com.publiccms.controller.api;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.publiccms.common.base.AbstractTemplateDirective;
import com.publiccms.common.base.BaseMethod;
import com.publiccms.common.constants.CommonConstants;
import com.publiccms.common.constants.Constants;
import com.publiccms.common.tools.CommonUtils;
import com.publiccms.common.tools.JavaDocUtils;
import com.publiccms.entities.sys.SysApp;
import com.publiccms.entities.sys.SysAppToken;
import com.publiccms.entities.sys.SysSite;
import com.publiccms.logic.component.site.DirectiveComponent;
import com.publiccms.logic.component.template.TemplateComponent;
import com.publiccms.logic.service.sys.SysAppService;
import com.publiccms.logic.service.sys.SysAppTokenService;

import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;

/**
 *
 * MethodController 方法统一分发
 *
 */
@RestController
public class MethodController {
    protected final Log log = LogFactory.getLog(getClass());
    private Map<String, BaseMethod> methodMap;
    private List<Map<String, String>> methodList = new ArrayList<>();
    private ObjectWrapper objectWrapper;
    /**
     *
     */
    protected static final Map<String, String> NEED_APP_TOKEN_MAP = new HashMap<String, String>() {
        private static final long serialVersionUID = 1L;
        {
            put(CommonConstants.ERROR, ApiController.NEED_APP_TOKEN);
        }
    };

    /**
     * 接口指令统一分发
     *
     * @param name
     * @param site
     * @param appToken
     * @param request
     * @return result
     */
    @RequestMapping("method/{name}")
    public Object method(@PathVariable String name, @RequestAttribute SysSite site,
            @RequestHeader(required = false) String appToken, HttpServletRequest request) {
        BaseMethod method = methodMap.get(name);
        if (null != method && method.httpEnabled()) {
            try {
                if (method.needAppToken()) {
                    if (null == appToken) {
                        appToken = request.getParameter(AbstractTemplateDirective.APP_TOKEN);
                    }
                    SysAppToken token = appTokenService.getEntity(appToken);
                    if (null == token || null != token.getExpiryDate() && CommonUtils.getDate().after(token.getExpiryDate())) {
                        return NEED_APP_TOKEN_MAP;
                    }
                    SysApp app = appService.getEntity(token.getAppId());
                    if (null == app || app.getSiteId() != site.getId() || CommonUtils.empty(app.getAuthorizedApis())
                            || !ArrayUtils.contains(StringUtils.split(app.getAuthorizedApis(), Constants.COMMA),
                                    method.getName())) {
                        return NEED_APP_TOKEN_MAP;
                    }
                }
                Map<String, Object> map = new HashMap<>();
                String[] parameters = request.getParameterValues("parameters");
                if (CommonUtils.notEmpty(parameters) && parameters.length >= method.minParametersNumber()) {
                    List<TemplateModel> list = new ArrayList<>();
                    for (String parameter : parameters) {
                        list.add(getObjectWrapper().wrap(parameter));
                    }
                    map.put("result", method.execute(request, list));
                    return map;
                } else if (CommonUtils.empty(parameters) && 0 == method.minParametersNumber()) {
                    map.put("result", method.execute(request, Collections.emptyList()));
                    return map;
                } else {
                    map.put(CommonConstants.ERROR, "parametersError");
                    return map;
                }
            } catch (TemplateModelException e) {
                log.error(e.getMessage(), e);
                Map<String, String> map = new HashMap<>();
                map.put(CommonConstants.ERROR, ApiController.EXCEPTION);
                return map;
            }
        } else {
            return ApiController.NOT_FOUND_MAP;
        }

    }

    /**
     * 接口列表
     *
     * @return result
     */
    @RequestMapping("methods")
    public List<Map<String, String>> methods() {
        return methodList;
    }

    /**
     * 接口初始化
     *
     * @param directiveComponent
     *
     */
    @Resource
    public void init(DirectiveComponent directiveComponent) {
        methodMap = directiveComponent.getMethodMap();
        for (Entry<String, BaseMethod> entry : methodMap.entrySet()) {
            if (entry.getValue().httpEnabled()) {
                Map<String, String> map = new HashMap<>();
                map.put("name", entry.getKey());
                map.put("minParameters", String.valueOf(entry.getValue().minParametersNumber()));
                map.put("doc", JavaDocUtils.getClassComment(entry.getValue().getClass().getName()));
                map.put("needAppToken", String.valueOf(entry.getValue().needAppToken()));
                map.put("needUserToken", String.valueOf(false));
                map.put("supportAdvanced", String.valueOf(false));
                methodList.add(map);
            }
        }
        Collections.sort(methodList, (o1, o2) -> o1.get("name").compareTo(o2.get("name")));
    }

    private ObjectWrapper getObjectWrapper() {
        if (null == objectWrapper) {
            objectWrapper = templateComponent.getWebConfiguration().getObjectWrapper();
        }
        return objectWrapper;
    }

    @Resource
    private SysAppTokenService appTokenService;
    @Resource
    private SysAppService appService;
    @Resource
    private TemplateComponent templateComponent;
}
