package ru.yandex.direct.web.entity.internaltools.controller;

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

import javax.annotation.ParametersAreNonnullByDefault;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import ru.yandex.direct.env.Environment;
import ru.yandex.direct.internaltools.core.container.InternalToolResult;
import ru.yandex.direct.internaltools.core.exception.InternalToolAccessDeniedException;
import ru.yandex.direct.internaltools.core.exception.InternalToolNotFoundException;
import ru.yandex.direct.internaltools.core.exception.InternalToolProcessingException;
import ru.yandex.direct.internaltools.core.exception.InternalToolValidationException;
import ru.yandex.direct.web.annotations.AllowedOperatorRoles;
import ru.yandex.direct.web.annotations.AllowedSubjectRoles;
import ru.yandex.direct.web.core.model.WebErrorResponse;
import ru.yandex.direct.web.core.model.WebResponse;
import ru.yandex.direct.web.core.security.captcha.DisableAutoCaptcha;
import ru.yandex.direct.web.core.security.netacl.AllowNetworks;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolCategoryDescription;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolDescriptionResponse;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolExtendedDescription;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolFileUploadingResponse;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolProcessingResponse;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolsFileDescription;
import ru.yandex.direct.web.entity.internaltools.model.InternalToolsListResponse;
import ru.yandex.direct.web.entity.internaltools.service.InternalToolsService;
import ru.yandex.direct.web.validation.kernel.ValidationResultConversionService;

import static ru.yandex.direct.common.net.NetworkName.INTERNAL;
import static ru.yandex.direct.rbac.RbacRole.INTERNAL_AD_ADMIN;
import static ru.yandex.direct.rbac.RbacRole.MANAGER;
import static ru.yandex.direct.rbac.RbacRole.MEDIA;
import static ru.yandex.direct.rbac.RbacRole.PLACER;
import static ru.yandex.direct.rbac.RbacRole.SUPER;
import static ru.yandex.direct.rbac.RbacRole.SUPERREADER;
import static ru.yandex.direct.rbac.RbacRole.SUPPORT;
import static ru.yandex.direct.utils.JsonUtils.toJson;
import static ru.yandex.direct.web.core.security.authentication.DirectCookieAuthProvider.PARAMETER_ULOGIN;

@ApiIgnore // не хотим показывать в swagger-е
@Controller
@ParametersAreNonnullByDefault
@RequestMapping(value = "/internal_tools", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@AllowNetworks({INTERNAL})
@AllowedOperatorRoles({SUPER, SUPERREADER, SUPPORT, PLACER, MEDIA, MANAGER, INTERNAL_AD_ADMIN})
@AllowedSubjectRoles({SUPER, SUPERREADER, SUPPORT, PLACER, MEDIA, MANAGER, INTERNAL_AD_ADMIN})
public class InternalToolsController {
    private static final String LABEL_PARAM = "tool_label";
    private static final Logger logger = LoggerFactory.getLogger(InternalToolsController.class);

    private final InternalToolsService internalToolsService;
    private final ValidationResultConversionService validationResultConversionService;

    @Autowired
    public InternalToolsController(InternalToolsService internalToolsService,
                                   ValidationResultConversionService validationResultConversionService) {
        this.internalToolsService = internalToolsService;
        this.validationResultConversionService = validationResultConversionService;
    }

    @GetMapping(value = "/")
    @DisableAutoCaptcha
    public String listReportsPage() {
        return "/static/internal_tools.html";
    }

    @DisableAutoCaptcha
    @RequestMapping(
            path = "/categories/list",
            method = RequestMethod.GET
    )
    @ResponseBody
    public WebResponse listReportsCategories(
            @SuppressWarnings("unused") @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {
        List<InternalToolCategoryDescription> categories = internalToolsService.getToolsCategories();
        return new InternalToolsListResponse()
                .withEnvironment(Environment.getCached().name())
                .withResult(categories);
    }

    @DisableAutoCaptcha
    @RequestMapping(
            path = "/tools/{" + LABEL_PARAM + "}/get",
            method = RequestMethod.GET
    )
    @ResponseBody
    public WebResponse getReport(
            @PathVariable(LABEL_PARAM) String toolLabel,
            @SuppressWarnings("unused") @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {
        InternalToolExtendedDescription description = internalToolsService.getToolDescription(toolLabel);
        return new InternalToolDescriptionResponse()
                .withEnvironment(Environment.getCached().name())
                .withResult(description);
    }

    @DisableAutoCaptcha
    @RequestMapping(
            path = "/tools/{" + LABEL_PARAM + "}/read",
            method = RequestMethod.GET
    )
    @ResponseBody
    public WebResponse processReadingReport(
            @PathVariable(LABEL_PARAM) String toolLabel,
            @RequestParam Map<String, Object> webRequest,
            @SuppressWarnings("unused") @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {
        InternalToolResult result = internalToolsService.processReadingTool(toolLabel, webRequest);
        return new InternalToolProcessingResponse().withResult(result);
    }

    @DisableAutoCaptcha
    @RequestMapping(
            path = "/tools/{" + LABEL_PARAM + "}/write",
            method = RequestMethod.POST,
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE
    )
    @ResponseBody
    public WebResponse processWritingReport(
            @PathVariable(LABEL_PARAM) String toolLabel,
            @RequestBody Map<String, Object> webRequest,
            @SuppressWarnings("unused") @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {
        InternalToolResult result = internalToolsService.processWritingTool(toolLabel, webRequest);
        return new InternalToolProcessingResponse().withResult(result);
    }

    @DisableAutoCaptcha
    @RequestMapping(
            path = "/tools/{" + LABEL_PARAM + "}/upload_file",
            method = RequestMethod.POST,
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE
    )
    @ResponseBody
    public WebResponse processUploadFileReport(
            @PathVariable(LABEL_PARAM) String toolLabel,
            @RequestParam("file") MultipartFile file,
            @SuppressWarnings("unused") @RequestParam(value = PARAMETER_ULOGIN, required = false) String subjectLogin) {
        InternalToolsFileDescription fileDescription = internalToolsService.saveFile(toolLabel, file);
        return new InternalToolFileUploadingResponse().withResult(fileDescription);
    }

    @ExceptionHandler(InternalToolNotFoundException.class)
    @ResponseBody
    public ResponseEntity<String> internalToolNotFoundExceptionHandler(InternalToolNotFoundException exception) {
        WebErrorResponse response = new WebErrorResponse("Tool not found", exception.getMessage());
        return new ResponseEntity<>(toJson(response), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(InternalToolProcessingException.class)
    @ResponseBody
    public ResponseEntity<String> internalToolProcessingExceptionHandler(InternalToolProcessingException exception) {
        logger.error("Error when processing tool", exception);
        WebErrorResponse response = new WebErrorResponse("Error when processing tool", exception.getMessage());
        return new ResponseEntity<>(toJson(response), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(InternalToolValidationException.class)
    @ResponseBody
    public ResponseEntity<String> internalToolValidationExceptionHandler(InternalToolValidationException exception) {
        WebResponse response;
        if (exception.getValidationResult() != null) {
            response = validationResultConversionService.buildValidationResponse(exception.getValidationResult());
        } else {
            response = new WebErrorResponse("Error occurred during validating input", exception.getMessage());
        }
        return new ResponseEntity<>(toJson(response), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(InternalToolAccessDeniedException.class)
    @ResponseBody
    public ResponseEntity<String> internalToolDeniedExceptionHandler(InternalToolAccessDeniedException exception) {
        WebErrorResponse response = new WebErrorResponse("Access denied", exception.getMessage());
        return new ResponseEntity<>(toJson(response), HttpStatus.FORBIDDEN);
    }
}
