package dev.dinauer.maven.maven.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import dev.dinauer.maven.jpa.maven.ArtifactId;
import dev.dinauer.maven.jpa.maven.Jar;
import dev.dinauer.maven.jpa.maven.Pom;
import dev.dinauer.maven.jpa.maven.Version;
import dev.dinauer.maven.maven.token.TokenService;
import dev.dinauer.maven.metadata.Metadata;
import dev.dinauer.maven.maven.core.model.FileHash;
import dev.dinauer.maven.maven.core.model.MavenContext;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.commons.codec.digest.DigestUtils;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;

@ApplicationScoped
public class Service
{
    @Inject
    VersionService versionService;

    @Inject
    ArtifactService artifactService;

    @Inject
    TokenService tokenService;

    @Transactional
    public Response upload(String path, byte[] body)
    {
        MavenContext mavenContext = MavenContextProvider.parse(path);
        switch (mavenContext.file().getType())
        {
            case JAR ->
            {
                if (FileHash.NONE.equals(mavenContext.file().getHash()))
                {
                    String md5 = DigestUtils.md5Hex(body);
                    String sha1 = DigestUtils.sha1Hex(body);
                    Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
                    version.getJars().add(new Jar().setJar(body).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(path).setFilename(mavenContext.file().getRaw()));
                    versionService.persist(version);
                    return Response.status(Response.Status.CREATED).build();
                }
                return Response.accepted().build();
            }
            case POM ->
            {
                if (FileHash.NONE.equals(mavenContext.file().getHash()))
                {
                    String md5 = DigestUtils.md5Hex(body);
                    String sha1 = DigestUtils.sha1Hex(body);
                    Version version = versionService.findOrCreate(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw());
                    version.setUploadedBy(tokenService.require());
                    if (version.getPom() == null)
                    {
                        version.setPom(new Pom().setPom(new String(body)).setMd5(md5).setSha1(sha1).setVersion(version).setUrl(path).setFilename(mavenContext.file().getRaw()));
                        versionService.persist(version);
                        return Response.status(Response.Status.CREATED).build();
                    }
                    return Response.status(Response.Status.CONFLICT).build();
                }
                return Response.accepted().build();
            }
            case XML ->
            {
                return Response.accepted().build();
            }
        }
        throw new RuntimeException();
    }

    @Transactional
    public Response serve(String path) throws JsonProcessingException
    {
        MavenContext mavenContext = MavenContextProvider.parse(path);
        switch (mavenContext.file().getType())
        {
            case JAR ->
            {
                Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
                Optional<Jar> optionalJar = version.getJarByFilename(mavenContext.file().getRaw());
                if (optionalJar.isEmpty())
                {
                    throw new NotFoundException();
                }
                switch (mavenContext.file().getHash())
                {
                    case SHA1 ->
                    {
                        return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getSha1()).build();
                    }
                    case MD5 ->
                    {
                        return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getMd5()).build();
                    }
                }
                version.setLastPulled(ZonedDateTime.now());
                return Response.status(Response.Status.OK).type(MediaType.APPLICATION_OCTET_STREAM).entity(optionalJar.get().getJar()).build();
            }
            case POM ->
            {
                Version version = versionService.findOptional(mavenContext.groupId(), mavenContext.artifactId(), mavenContext.version().getRaw()).orElseThrow();
                version.setLastPulled(ZonedDateTime.now());
                version.incrementPullCount();
                return Response.status(Response.Status.OK).type(MediaType.APPLICATION_XML).entity(version.getPom().getPom()).build();
            }
            case XML ->
            {
                Optional<ArtifactId> artifactOptional = artifactService.find(mavenContext.groupId(), mavenContext.artifactId());
                if (artifactOptional.isPresent())
                {
                    ArtifactId artifact = artifactOptional.get();
                    String metadata = new XmlMapper().writeValueAsString(new Metadata(artifact.getGroupId(), artifact.getArtifactId(), null, artifact.getUpdatedAt().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))));
                    switch (mavenContext.file().getHash())
                    {
                        case SHA1 ->
                        {
                            return Response.ok().type(MediaType.TEXT_PLAIN).entity(DigestUtils.sha1Hex(metadata)).build();
                        }
                        case MD5 ->
                        {
                            return Response.ok().type(MediaType.TEXT_PLAIN).entity(DigestUtils.md5Hex(metadata)).build();
                        }
                    }
                    return Response.ok().type(MediaType.APPLICATION_XML).entity(metadata).build();
                }
                return Response.status(Response.Status.NOT_FOUND).build();
            }
        }
        throw new RuntimeException();
    }
}
