package pwc.taxtech.atms.controller;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import pwc.taxtech.atms.dto.ApiResultDto;
import pwc.taxtech.atms.exception.ApiException;
import pwc.taxtech.atms.exception.ApplicationException;
import pwc.taxtech.atms.exception.ServiceException;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@EnableWebMvc
@ControllerAdvice
public class AtmsExceptionHandler extends ResponseEntityExceptionHandler {
    private static Logger LOGGER = LoggerFactory.getLogger(AtmsExceptionHandler.class);

    @ExceptionHandler(value = {
            ApplicationException.class,
            ApiException.class
    })
    protected ResponseEntity<Object> handleExceptions(Exception ex) throws ServiceException {
        LOGGER.error("Rest Exception!", ex);
        ex.printStackTrace();
        if (ex.getMessage() != null) {
            LOGGER.debug("Rest Exception for {}", ex.getMessage());
            LOGGER.info("Rest Exception for {]", ex.getMessage());
        }

        if (ex instanceof ApplicationException) {
            ex.printStackTrace();
            return handleApplicationException((ApplicationException) ex);
        } else if (ex instanceof ServiceException) {
            ex.printStackTrace();
            return handleServiceException((ServiceException) ex);
        } else if (ex instanceof ApiException) {
            ex.printStackTrace();
            return ((ApiException) ex).handle();
        } else {
            ex.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }


    @ExceptionHandler(value = AccessDeniedException.class)
    public void accessDeniedHandle(AccessDeniedException accessDeniedException, HttpServletResponse response) {
        accessDeniedException.printStackTrace();
        //noinspection Duplicates
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=UTF-8");
            response.setStatus(403);
            response.getWriter().write(JSON.toJSONString(ApiResultDto.fail(accessDeniedException.getMessage())));
        } catch (IOException e) {
            logger.error("accessDenied error.", e);
        }
    }

    @ExceptionHandler(value = Throwable.class)
    public void handle(Throwable throwable, HttpServletResponse response) {
        throwable.printStackTrace();
        logger.error("handle : ", throwable);
        //noinspection Duplicates
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ApiResultDto.fail(throwable.getMessage())));
        } catch (IOException e) {
            logger.error("customHandle error.", e);
        }
    }

    @ExceptionHandler(value = ServiceException.class)
    public void customHandle(pwc.taxtech.atms.common.ServiceException exception, HttpServletResponse response) {
        //noinspection Duplicates
        logger.error(exception);
        try {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(ApiResultDto.fail(exception.getMessage())));
        } catch (IOException e) {
            logger.error("customHandle error.", e);
        }
    }

    private ResponseEntity<Object> handleApplicationException(ApplicationException ex) {
        throw ex;
    }

    private ResponseEntity<Object> handleServiceException(ServiceException ex) throws ServiceException {
        throw ex;
    }

}