package objective.taskboard.jira.deployment;

import static objective.taskboard.jira.data.JiraIssue.FieldBuilder.byName;
import static objective.taskboard.jira.data.JiraIssue.FieldBuilder.byNames;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;

import objective.taskboard.data.User;
import objective.taskboard.jira.CustomJiraPluginInstalledVerifier.CustomJiraPluginNotInstalledException;
import objective.taskboard.jira.JiraService;
import objective.taskboard.jira.JiraUserNotFoundException;
import objective.taskboard.jira.data.FieldsRequiredInTransition;
import objective.taskboard.jira.data.JiraIssue.FieldBuilder;
import objective.taskboard.jira.data.JiraIssue.Input;
import objective.taskboard.jira.data.JiraUser;
import objective.taskboard.jira.data.plugin.UserDetail;
import objective.taskboard.jira.endpoint.JiraEndpointAsLoggedInUser;
import objective.taskboard.jira.endpoint.JiraEndpointAsMaster;
import objective.taskboard.jira.properties.JiraProperties;
import objective.taskboard.utils.IOUtilities;
import retrofit.RetrofitError;
import retrofit.client.Response;

@Component
@ConditionalOnProperty(prefix="jira", name = "deployment-type", havingValue = "SERVER", matchIfMissing = true)
public class JiraHostServer implements JiraHost {
    static final String MSG_UNAUTHORIZED = "Authentication failed";
    static final String MSG_FORBIDDEN = "Jira account is requesting a captcha challenge. Try logging in Jira";

    private static final Logger log = LoggerFactory.getLogger(JiraHostServer.class);

    private final JiraEndpointAsMaster jiraEndpointAsMaster;

    private final JiraEndpointAsLoggedInUser jiraEndpointAsUser;
    
    private JiraProperties properties;

    @Autowired
    public JiraHostServer(JiraProperties properties, JiraEndpointAsMaster jiraEndpointAsMaster, JiraEndpointAsLoggedInUser jiraEndpointAsUser) {
        this.properties = properties;
        this.jiraEndpointAsMaster = jiraEndpointAsMaster;
        this.jiraEndpointAsUser = jiraEndpointAsUser;
    }

    
    @Override
    public void validateUsername(String username) {
    }

    @Override
    public void checkAuthenticatioError(HttpStatus httpStatus, RetrofitError ex) {
        if (httpStatus == HttpStatus.UNAUTHORIZED)
            throw new AccessDeniedException(MSG_UNAUTHORIZED, ex);

        if (httpStatus == HttpStatus.FORBIDDEN)
            throw new AccessDeniedException(MSG_FORBIDDEN, ex);
    }

    @Override
    public JiraUser getJiraUser(String username) {
        try {
            return jiraEndpointAsUser.request(JiraUser.Service.class).get(username);
        }catch(RetrofitError e) {
            if (e.getResponse() != null && e.getResponse().getStatus() == HttpStatus.NOT_FOUND.value())
                throw new JiraUserNotFoundException(username);
            throw e;
        }
    }

    @Override
    public JiraUser getJiraUserAsMaster(String username) {
        return jiraEndpointAsMaster.request(JiraUser.Service.class).get(username);
    }

    @Override
    public List<FieldsRequiredInTransition> getFieldsRequiredInTransitions(String issueKey, List<Long> transitionIds) {
        return jiraEndpointAsUser.request(FieldsRequiredInTransition.Service.class).post(issueKey, transitionIds);
    }

    @Override
    public void validateCustomJiraPlugin(JiraProperties properties) {
        try {
            jiraEndpointAsMaster.request(UserDetail.Service.class).get(properties.getLousa().getUsername());
        } catch (RetrofitError ex) {
            evaluateError(ex.getResponse());
            log.error("Failed to get response from jira", ex);
        }
    }

    @Override
    public String getType() {
        return "server";
    }

    private void evaluateError(Response response) {
        if (response == null)
            return;

        if (response.getStatus() != HttpStatus.NOT_FOUND.value())
            return;

        boolean htmlResponse = response.getHeaders().stream()
                .filter(h -> HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(h.getName()))
                .anyMatch(h -> h.getValue().contains("text/html"));
        if(htmlResponse)
            throw new CustomJiraPluginNotInstalledException();

        try (InputStream body = response.getBody().in()) {
            String content = IOUtilities.resourceAsString(body);
            if (content.contains("null for uri")) {
                throw new CustomJiraPluginNotInstalledException();
            }
        } catch (IOException ex) {
            log.error("Could not read response content", ex);
        }
    }

    @Override
    public Input assignIssueToUser(String issueKey, List<User> assigneeList) {
        return new AssignIssueToUserAction(properties, assigneeList).createRequest();
    }
    
    static class AssignIssueToUserAction extends JiraService.AssignIssueToUserAction {

        public AssignIssueToUserAction(JiraProperties properties, List<User> assignees) {
            super(properties, assignees);
        }

        @Override
        public FieldBuilder buildAssigneeField(String assignee) {
            return byName(assignee);
        }

        @Override
        protected FieldBuilder buildCoAssignees(List<String> coAssigneesNames) {
            return byNames(coAssigneesNames);
        }
    }
}
