package pwc.taxtech.atms.web.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSON;

import pwc.taxtech.atms.common.CheckState;
import pwc.taxtech.atms.dto.AtmsTokenDto;
import pwc.taxtech.atms.dto.LoginInputDto;
import pwc.taxtech.atms.dto.LoginOutputDto;
import pwc.taxtech.atms.dto.OperationResultDto;
import pwc.taxtech.atms.dto.OrganizationStructureDto;
import pwc.taxtech.atms.web.AtmsWebSettings;

/** @see PwC.Tax.Tech.Atms.Web\Controllers\AccountController.cs */
@Controller
@RequestMapping("/Account")
public class AccountController {

    private static Logger logger = LoggerFactory.getLogger(AccountController.class);

    @Autowired
    private AtmsWebSettings atmsWebSettings;

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/LogOn", method = RequestMethod.POST)
    public @ResponseBody LoginOutputDto login(@RequestBody LoginInputDto input, HttpServletResponse response)
            throws UnsupportedEncodingException {
        logger.debug("login start");
        if (input == null || !StringUtils.hasText(input.getEmail())) {
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("Please enter the valid user name for your account.");
            errorReturn.setCheckState(CheckState.EmptyUserName.value());
            logger.debug("空的用户名");
            return errorReturn;
        }
        if (!StringUtils.hasText(input.getPassword())) {
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("Please enter your password.");
            errorReturn.setCheckState(CheckState.EmptyPassword.value());
            logger.debug("空的密码");
            return errorReturn;
        }
        OperationResultDto<LoginOutputDto> operationResultDto = null;
        try {
            long start = System.currentTimeMillis();
            logger.debug("准备调用atms-api的login接口");
            operationResultDto = callApiUserLogin(input.getEmail(), input.getPassword());
            logger.debug("atms-api的login接口返回,用时[{}ms]", System.currentTimeMillis() - start);
        } catch (RestClientException e) {
            logger.error("调用atms-api的login接口出错:" + e, e);
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("Generate auth token failed, please check if WebApi is running");
            errorReturn.setCheckState(CheckState.UnKnown.value());
            return errorReturn;
        } catch (Exception e) {
            logger.error("Unknown error occures, 调用atms-api的login接口出错:" + e, e);
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("Unknown error occures " + e.getMessage());
            errorReturn.setCheckState(CheckState.UnKnown.value());
            return errorReturn;
        }
        Assert.notNull(operationResultDto.getResult(), "Null value of operationResultDto.getResult()");
        LoginOutputDto resultLoginOutputDto = operationResultDto.getData();
        if (!operationResultDto.getResult()) {
            logger.info("登录失败, email:{}, password.length:{}", input.getEmail(), input.getPassword().length());
            return resultLoginOutputDto;
        }
        logger.info("登录成功, email:{}, password.length:{}", input.getEmail(), input.getPassword().length());
        if (!CheckState.Success.value().equals(resultLoginOutputDto.getCheckState())) {
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("服务端返回状态异常");
            logger.info("登录成功但是CheckState有异常, email:{}, password.length:{}, data.checkState:{}", input.getEmail(),
                    input.getPassword().length(), resultLoginOutputDto.getCheckState());
            errorReturn.setCheckState(CheckState.UnKnown.value());
            return errorReturn;
        }

        // CheckResult checkResult = validate(input)
        // UserDto dummyUser = new UserDto();
        // dummyUser.setUniqueId("05cae670-0b10-4b60-9697-04aed2c0da85");
        // dummyUser.setLoginName(input.getEmail());
        // dummyUser.setPassword(input.getPassword());
        // dummyUser.setHasValidPeriod(false);
        // LogOnModel loginModel = new LogOnModel();
        // loginModel.setCheckState(CheckState.SUCCESS.value());
        // loginModel.setMessage("login success");
        // loginModel.setUser(dummyUser);
        // loginModel.setApiHost(apiUrl);
        // AtmsTokenDto cookieModel = new AtmsTokenDto();
        // cookieModel.setAccess_token(
        // "5VEIJ9Nv_M6ppQlgepJcCYXhZPkY_pcgbKpVt-P4ZOOqpFWQ0DgMtymbOw26jrZYoFnoQiyzmv29wpeBWaAHnE4nYRNZ_5ZsvG_59kYh_bWd6c42qaiPOdZQN_s9VZjqEfdn96Ucfa-gAbKJANMUiewrzecl2v4ugMlid0XuzcKYaKsceESR36pfRDMrEf5Q7xqx-A");
        // cookieModel.setToken_type("bearer");
        // cookieModel.setExpires_in(1200000L);
        // cookieModel.setApi_host(apiUrl);
        // cookieModel.setVat_api_host("https://cnshaappuwv023:30005");
        // cookieModel.setTp_url("https://cnshaappuwv023:35001");
        // cookieModel.setVersion("1.0.0.0");
        // cookieModel.setUser_name("admin");
        // cookieModel.setLocal_name("admin");
        // cookieModel.setNeed_change_password(false);
        // cookieModel.setIs_external_user(false);
        // cookieModel.setUser_id("66933E7B-DA75-4B2E-B7D6-AB65DCA20D50");

        AtmsTokenDto token = resultLoginOutputDto.getToken();
        if (token == null || !StringUtils.hasText(token.getAccess_token())) {
            LoginOutputDto errorReturn = new LoginOutputDto();
            errorReturn.setMessage("服务端返回Token异常");
            logger.info("登录成功但是Token有异常, email:{}, password.length:{}, data.checkState:{}", input.getEmail(),
                    input.getPassword().length(), resultLoginOutputDto.getCheckState());
            errorReturn.setCheckState(CheckState.UnKnown.value());
            return errorReturn;
        }

        // 字段api_host应该由atms-web来定义
        token.setApi_host(atmsWebSettings.getApiUrl());
        resultLoginOutputDto.setApiHost(atmsWebSettings.getApiUrl());
        String cookieString = JSON.toJSONString(resultLoginOutputDto.getToken());
        String cookieValue = URLEncoder.encode(cookieString, "UTF-8");
        Cookie cookie = new Cookie("AtmsApiToken", cookieValue);
        cookie.setPath("/");
        // TODO get maxAge from properties
        cookie.setMaxAge(atmsWebSettings.getCookieMaxAgeSeconds());
        response.addCookie(cookie);
        // 在输出的json中删除Token对象,因为前端不需要这个字段
        resultLoginOutputDto.setToken(null);
        return resultLoginOutputDto;
    }

    private OperationResultDto<LoginOutputDto> callApiUserLogin(String email, String password) {
        String url = atmsWebSettings.getApiUrl() + "/api/v1/user/login";
        logger.debug("Print url:{}", url);
        ParameterizedTypeReference<OperationResultDto<LoginOutputDto>> parameterizedTypeReference = new ParameterizedTypeReference<OperationResultDto<LoginOutputDto>>() {
        };
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        LoginInputDto loginInputDto = new LoginInputDto();
        loginInputDto.setEmail(email);
        loginInputDto.setPassword(password);
        HttpEntity<LoginInputDto> requestEntity = new HttpEntity<>(loginInputDto, headers);
        ResponseEntity<OperationResultDto<LoginOutputDto>> responseEntity = restTemplate.exchange(url, HttpMethod.POST,
                requestEntity, parameterizedTypeReference);

        Assert.notNull(responseEntity, "Null responseEntity");
        Assert.notNull(responseEntity.getBody(), "Null responseEntity.body");
        OperationResultDto<LoginOutputDto> operationResultDto = responseEntity.getBody();
        logger.debug("print OperationResultDto<LoginOutputDto> as JSON:{}",
                JSON.toJSONString(operationResultDto, true));
        // Assert.state(operationResultDto.getResult() != null &&
        // operationResultDto.getResult(),
        // "operationResultDto.getResult() is false");
        return operationResultDto;

    }

    @RequestMapping(value = { "/LogOut", "/Logout" }, produces = "text/html;charset=UTF-8")
    public ModelAndView logout(HttpServletRequest request, HttpServletResponse response) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 删除会话
            session.invalidate();
        }
        Cookie cookie = new Cookie("AtmsApiToken", "");
        cookie.setPath("/");
        cookie.setMaxAge(0);
        // 删除Cookie
        response.addCookie(cookie);
        return new ModelAndView("logon");
    }
    
    
    @RequestMapping(value = { "/LogOn", "/Logon" }, produces = "text/html;charset=UTF-8", method = RequestMethod.GET)
    public ModelAndView LogOnGet(HttpServletRequest request, HttpServletResponse response) {
        return new ModelAndView("logon");
    }

    @RequestMapping(value = "/PingApi", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public OperationResultDto<OrganizationStructureDto> pingApi() {
        String url = atmsWebSettings.getApiUrl() + "/PingApi";
        logger.debug("Print url:{}", url);
        ParameterizedTypeReference<OperationResultDto<OrganizationStructureDto>> parameterizedTypeReference = new ParameterizedTypeReference<OperationResultDto<OrganizationStructureDto>>() {
        };
        ResponseEntity<OperationResultDto<OrganizationStructureDto>> responseEntity = restTemplate.exchange(url,
                HttpMethod.GET, null, parameterizedTypeReference);
        return responseEntity.getBody();
    }

    @RequestMapping(value = "/CheckLoginStatus", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public Boolean checkLoginStatus(@CookieValue(value = "AtmsApiToken", required = false) String atmsApiToken,
            HttpServletResponse response) throws UnsupportedEncodingException {
        if (StringUtils.hasText(atmsApiToken)) {
            Cookie newCookie = new Cookie("AtmsApiToken", URLEncoder.encode(atmsApiToken, "UTF-8"));
            newCookie.setPath("/");
            newCookie.setMaxAge(atmsWebSettings.getCookieMaxAgeSeconds());
            response.addCookie(newCookie);
            return true;
        }
        return false;
    }

    @SuppressWarnings("rawtypes")
    @RequestMapping(value = "/ForgetPassword", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public OperationResultDto<Object> forgetPassword(@RequestBody(required = false) LoginInputDto input) {
        logger.debug("enter ForgetPassword");
        Assert.notNull(input, "Null input object");
        Assert.hasText(input.getEmail(), "Empty email");
        logger.debug("print email:{}", input.getEmail());
        final String targetApi = "/api/v1/Account/ForgetPassword";

        String url = atmsWebSettings.getApiUrl() + targetApi;
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        builder.queryParam("mail", input.getEmail());
        builder.build().encode().toUriString();

        try {
            OperationResultDto operationResultDto = restTemplate.getForObject(url, OperationResultDto.class);
            if (operationResultDto != null && operationResultDto.getResult() != null
                    && operationResultDto.getResult()) {
                logger.info("return success by target api [{}]: {}", targetApi);
                return new OperationResultDto<>(true);
            } else {
                logger.error("return false by target api [{}]: {}", targetApi);
                return new OperationResultDto<>(false);
            }
        } catch (Exception e) {
            logger.error("error calling target api [{}]: {}", targetApi, e.toString());
            return new OperationResultDto<>(false);
        }
    }

}