package org.zanata.rest.service;

import static org.zanata.common.EntityStatus.OBSOLETE;
import static org.zanata.common.EntityStatus.READONLY;

import java.net.URI;

import javax.annotation.Nonnull;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.util.HttpHeaderNames;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.security.Identity;
import org.zanata.common.ProjectType;
import org.zanata.dao.AccountDAO;
import org.zanata.dao.ProjectDAO;
import org.zanata.model.HAccount;
import org.zanata.model.HProject;
import org.zanata.model.HProjectIteration;
import org.zanata.rest.MediaTypes;
import org.zanata.rest.NoSuchEntityException;
import org.zanata.rest.dto.Link;
import org.zanata.rest.dto.Project;
import org.zanata.rest.dto.ProjectIteration;

import com.google.common.base.Objects;

@Name("projectService")
@Path(ProjectResource.SERVICE_PATH)
@Transactional
public class ProjectService implements ProjectResource {
    /** Project Identifier. */
    @PathParam("projectSlug")
    String projectSlug;

    @HeaderParam(HttpHeaderNames.ACCEPT)
    @DefaultValue(MediaType.APPLICATION_XML)
    @Context
    private MediaType accept;

    @Context
    private UriInfo uri;
    @Context
    private Request request;

    @In
    ProjectDAO projectDAO;

    @In
    AccountDAO accountDAO;

    @In
    Identity identity;

    @In
    ETagUtils eTagUtils;

    @SuppressWarnings("null")
    @Nonnull
    public String getProjectSlug() {
        return projectSlug;
    }

    @Override
    public Response head() {
        EntityTag etag = eTagUtils.generateTagForProject(projectSlug);
        ResponseBuilder response = request.evaluatePreconditions(etag);
        if (response != null) {
            return response.build();
        }
        return Response.ok().tag(etag).build();
    }

    @Override
    public Response get() {
        try {
            EntityTag etag = eTagUtils.generateTagForProject(getProjectSlug());

            ResponseBuilder response = request.evaluatePreconditions(etag);
            if (response != null) {
                return response.build();
            }

            HProject hProject = projectDAO.getBySlug(getProjectSlug());
            Project project = toResource(hProject, accept);
            return Response.ok(project).tag(etag).build();
        } catch (NoSuchEntityException e) {
            return Response.status(Status.NOT_FOUND).build();
        }
    }

    @Override
    public Response put(Project project) {
        ResponseBuilder response;
        EntityTag etag;

        HProject hProject = projectDAO.getBySlug(getProjectSlug());

        if (hProject == null) { // must be a create operation
            response = request.evaluatePreconditions();
            if (response != null) {
                return response.build();
            }
            hProject = new HProject();
            hProject.setSlug(projectSlug);
            // pre-emptive entity permission check
            identity.checkPermission(hProject, "insert");

            response = Response.created(uri.getAbsolutePath());
        }
        // Project is obsolete
        else if (Objects.equal(hProject.getStatus(), OBSOLETE)) {
            return Response.status(Status.FORBIDDEN)
                    .entity("Project '" + projectSlug + "' is obsolete.")
                    .build();
        }
        // Project is ReadOnly
        else if (Objects.equal(hProject.getStatus(), READONLY)) {
            return Response.status(Status.FORBIDDEN)
                    .entity("Project '" + projectSlug + "' is read-only.")
                    .build();
        } else {
            // must be an update operation
            // pre-emptive entity permission check
            identity.checkPermission(hProject, "update");
            etag = eTagUtils.generateTagForProject(projectSlug);
            response = request.evaluatePreconditions(etag);
            if (response != null) {
                return response.build();
            }

            response = Response.ok();
        }

        // null project type accepted for compatibility with old clients
        if (project.getDefaultType() != null) {
            if (project.getDefaultType().isEmpty()) {
                return Response.status(Status.BAD_REQUEST)
                        .entity("No valid default project type was specified.")
                        .build();
            }

            try {
                ProjectType.getValueOf(project.getDefaultType());
            } catch (Exception e) {
                String validTypes =
                        StringUtils.join(ProjectType.values(), ", ");
                return Response
                        .status(Status.BAD_REQUEST)
                        .entity("Project type \"" + project.getDefaultType()
                                + "\" not valid for this server."
                                + " Valid types: [" + validTypes + "]").build();
            }
        }

        transfer(project, hProject);

        hProject = projectDAO.makePersistent(hProject);
        projectDAO.flush();

        if (identity != null && hProject.getMaintainers().isEmpty()) {
            HAccount hAccount =
                    accountDAO.getByUsername(identity.getCredentials()
                            .getUsername());
            if (hAccount != null && hAccount.getPerson() != null) {
                hProject.getMaintainers().add(hAccount.getPerson());
            }
            projectDAO.flush();
        }

        etag = eTagUtils.generateTagForProject(projectSlug);
        return response.tag(etag).build();

    }

    private static void transfer(Project from, HProject to) {
        to.setName(from.getName());
        to.setDescription(from.getDescription());
        if (from.getDefaultType() != null) {
            ProjectType projectType;
            try {
                projectType = ProjectType.getValueOf(from.getDefaultType());
            } catch (Exception e) {
                projectType = null;
            }

            if (projectType != null) {
                to.setDefaultProjectType(projectType);
            }
        }
        // TODO Currently all Projects are created as Current
        // to.setStatus(from.getStatus());

        // keep source URLs unless they are specifically overwritten
        if (from.getSourceViewURL() != null) {
            to.setSourceViewURL(from.getSourceViewURL());
        }
        if (from.getSourceCheckoutURL() != null) {
            to.setSourceCheckoutURL(from.getSourceCheckoutURL());
        }
    }

    private static void transfer(HProject from, Project to) {
        to.setId(from.getSlug());
        to.setName(from.getName());
        to.setDescription(from.getDescription());
        to.setStatus(from.getStatus());
        if (from.getDefaultProjectType() != null) {
            to.setDefaultType(from.getDefaultProjectType().toString());
        }
        to.setSourceViewURL(from.getSourceViewURL());
        to.setSourceCheckoutURL(from.getSourceCheckoutURL());
    }

    private static Project toResource(HProject hProject, MediaType mediaType) {
        Project project = new Project();
        transfer(hProject, project);
        for (HProjectIteration pIt : hProject.getProjectIterations()) {
            ProjectIteration iteration = new ProjectIteration();
            ProjectIterationService.transfer(pIt, iteration);

            iteration
                    .getLinks(true)
                    .add(new Link(
                            URI.create("iterations/i/" + pIt.getSlug()),
                            "self",
                            MediaTypes
                                    .createFormatSpecificType(
                                            MediaTypes.APPLICATION_ZANATA_PROJECT_ITERATION,
                                            mediaType)));
            project.getIterations(true).add(iteration);
        }
        return project;
    }

}

@@ -5520,93 +5520,8 @@
 );%0A%0A
-        hProject = projectDAO.makePersistent(hProject);%0A        projectDAO.flush();%0A%0A
     
@@ -5882,32 +5882,39 @@
           %7D%0A    
+    %7D%0A%0A
         projectD
@@ -5920,27 +5920,33 @@
 DAO.
-flush();%0A        %7D%0A
+makePersistent(hProject);
 %0A   

create project REST service will fail on adding maintainer