package com.atlassian.stash.internal.web.admin;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.stash.internal.config.log.InternalLoggingService;
import com.atlassian.stash.internal.server.InternalApplicationPropertiesService;
import com.atlassian.stash.internal.server.ProfilingService;
import com.atlassian.stash.internal.web.auth.IgnoresXsrf;
import com.atlassian.stash.internal.web.logging.RestProfiling;
import com.atlassian.stash.internal.web.soy.StashSoyResponseBuilder;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionService;
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.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
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.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.io.IOException;

import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
 * Controller for logging and profiling.
 * NOTE: this controller also contains REST methods.
 */
@Controller
@RequestMapping("/admin")
public class LoggingController {

    public static final String PATH_LOGGING = "logging";
    public static final String PATH_PROFILING = "profiling";
    private static final String VIEW = "bitbucket.internal.admin.logging";

    private final InternalApplicationPropertiesService applicationPropertiesService;
    private final I18nService i18nService;
    private final InternalLoggingService loggingService;
    private final PermissionService permissionService;
    private final ProfilingService profilingService;

    @Autowired
    public LoggingController(InternalApplicationPropertiesService applicationPropertiesService,
                             I18nService i18nService,
                             InternalLoggingService loggingService,
                             PermissionService permissionService,
                             ProfilingService profilingService) {
        this.applicationPropertiesService = applicationPropertiesService;
        this.i18nService = i18nService;
        this.loggingService = loggingService;
        this.permissionService = permissionService;
        this.profilingService = profilingService;
    }

    @RequestMapping(method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE,
            value = PATH_PROFILING)
    public ResponseEntity<RestProfiling> getProfiling() {
        if (!permissionService.hasGlobalPermission(Permission.ADMIN)) {
            throw new AccessDeniedException(i18nService.getMessage("bitbucket.web.config.logging.adminonly"));
        }
        boolean enabled = profilingService.isEnabled();
        return new ResponseEntity<>(new RestProfiling(enabled), null, HttpStatus.OK);
    }

    @RequestMapping(method = RequestMethod.POST,
            consumes = MediaType.APPLICATION_JSON_VALUE,
            value = PATH_PROFILING)
    @IgnoresXsrf // This is called from REST not a form, so won't have the usual XSRF token
    public ResponseEntity<String> setProfiling(@RequestBody String json) {
        if (!permissionService.hasGlobalPermission(Permission.ADMIN)) {
            throw new AccessDeniedException(i18nService.getMessage("bitbucket.web.config.logging.adminonly"));
        }
        try {
            RestProfiling restProfiling = RestProfiling.createFromJson(json, RestProfiling.class);
            if (restProfiling.getEnabled()) {
                profilingService.enable();
            } else {
                profilingService.disable();
            }
            return new ResponseEntity<>(null, null, HttpStatus.ACCEPTED);
        } catch (IOException e) {
            return new ResponseEntity<>(null, null, HttpStatus.BAD_REQUEST);
        }
    }

    @RequestMapping(method = RequestMethod.GET, value = PATH_LOGGING)
    public ModelAndView showLoggingSettings() {

        return new StashSoyResponseBuilder(VIEW)
                .put("settings", loadSettings())
                .put("logFileName", "atlassian-bitbucket.log")
                .put("profilerLogFileName", "atlassian-bitbucket-profiler.log")
                .build();
    }

    @RequestMapping(method = POST, value = PATH_LOGGING)
    public ModelAndView update(Form form, Errors errors, RedirectAttributes redirectAttributes) {
        // Persist the settings.
        applicationPropertiesService.setDebugLoggingEnabled(form.isEnableDebugLogging());
        applicationPropertiesService.setProfilingEnabled(form.isEnableProfiling());

        // apply the settings
        if (form.isEnableProfiling()) {
            profilingService.enable();
        } else {
            profilingService.disable();
        }
        if (form.isEnableDebugLogging()) {
            loggingService.enableDebugLogging();
        } else {
            loggingService.disableDebugLogging();
        }

        redirectAttributes.addFlashAttribute("saved", true);
        return new StashSoyResponseBuilder("redirect:/admin/logging").build();
    }

    private Form loadSettings() {
        return new Form(applicationPropertiesService.isProfilingEnabled(),
                        applicationPropertiesService.isDebugLoggingEnabled());
    }

    public static class Form {
        private boolean enableProfiling;
        private boolean enableDebugLogging;

        public Form() {
        }

        public Form(boolean enableProfiling, boolean enableDebugLogging) {
            this.enableProfiling = enableProfiling;
            this.enableDebugLogging = enableDebugLogging;
        }

        public boolean isEnableDebugLogging() {
            return enableDebugLogging;
        }

        public boolean isEnableProfiling() {
            return enableProfiling;
        }

        public void setEnableDebugLogging(boolean enableDebugLogging) {
            this.enableDebugLogging = enableDebugLogging;
        }

        public void setEnableProfiling(boolean enableProfiling) {
            this.enableProfiling = enableProfiling;
        }
    }
}
