package org.kar.karusic.api;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.FormDataOptional;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.DataTools;
import org.kar.karusic.model.Track;
import org.kar.karusic.model.Track.TrackChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path("/track")
@Produces({ MediaType.APPLICATION_JSON })
public class TrackResource {
	private static final Logger LOGGER = LoggerFactory.getLogger(TrackResource.class);
	static final TrackChecker CHECKER = new TrackChecker();

	@GET
	@Path("{id}")
	@RolesAllowed("USER")
	public Track get(@PathParam("id") final Long id) throws Exception {
		return DataAccess.get(Track.class, id);
	}

	@GET
	@RolesAllowed("USER")
	public List<Track> gets() throws Exception {
		return DataAccess.gets(Track.class);
	}

	@POST
	@RolesAllowed("ADMIN")
	@Consumes(MediaType.APPLICATION_JSON)
	public Track post(final Track data) throws Exception {
		return DataAccess.insert(data, new CheckFunction(CHECKER));
	}

	@PATCH
	@Path("{id}")
	@RolesAllowed("ADMIN")
	@Consumes(MediaType.APPLICATION_JSON)
	public Track patch(@PathParam("id") final Long id, @AsyncType(Track.class) final String jsonRequest) throws Exception {
		DataAccess.updateWithJson(Track.class, id, jsonRequest, new CheckFunction(CHECKER));
		return DataAccess.get(Track.class, id);
	}

	@DELETE
	@Path("{id}")
	@RolesAllowed("ADMIN")
	public void remove(@PathParam("id") final Long id) throws Exception {
		DataAccess.delete(Track.class, id);
	}

	@POST
	@Path("{id}/artist/{artistId}")
	@RolesAllowed("ADMIN")
	@Consumes({ MediaType.MULTIPART_FORM_DATA })
	public Track addTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
		try (DBAccess db = DBAccess.createInterface()) {
			AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
			return DataAccess.get(Track.class, id);
		}
	}

	@DELETE
	@Path("{id}/artist/{trackId}")
	@RolesAllowed("ADMIN")
	public Track removeTrack(@PathParam("id") final Long id, @PathParam("artistId") final Long artistId) throws Exception {
		try (DBAccess db = DBAccess.createInterface()) {
			AddOnDataJson.removeLink(db, Track.class, "id", id, "artist", artistId);
			return DataAccess.get(Track.class, id);
		}
	}

	@POST
	@Path("{id}/cover")
	@RolesAllowed("ADMIN")
	@Consumes({ MediaType.MULTIPART_FORM_DATA })
	@TypeScriptProgress
	public Track uploadCover(@PathParam("id") final Long id, @FormDataParam("uri") final String uri, @FormDataParam("file") final InputStream fileInputStream,
			@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
		try (DBAccess db = DBAccess.createInterface()) {
			if (uri != null) {
				DataTools.uploadCoverFromUri(db, Track.class, id, uri);
			} else {
				DataTools.uploadCover(db, Track.class, id, fileInputStream, fileMetaData);
			}
			return DataAccess.get(Track.class, id);
		}
	}

	@DELETE
	@Path("{id}/cover/{coverId}")
	@RolesAllowed("ADMIN")
	public Track removeCover(@PathParam("id") final Long id, @PathParam("coverId") final ObjectId coverId) throws Exception {
		try (DBAccess db = DBAccess.createInterface()) {
			AddOnDataJson.removeLink(db, Track.class, "id", id, "covers", coverId);
			return db.get(Track.class, id);
		}
	}

	@POST
	@Path("upload/")
	@RolesAllowed("ADMIN")
	@Consumes({ MediaType.MULTIPART_FORM_DATA })
	@AsyncType(Track.class)
	@TypeScriptProgress
	public Response uploadTrack( //
			@FormDataParam("title") String title, //
			@FormDataOptional @AsyncType(Long.class) @FormDataParam("genderId") String genderId, //
			@FormDataOptional @AsyncType(Long.class) @FormDataParam("artistId") String artistId, //
			@FormDataOptional @AsyncType(Long.class) @FormDataParam("albumId") String albumId, //
			@FormDataOptional @AsyncType(Long.class) @FormDataParam("trackId") String trackId, //
			@FormDataParam("file") final InputStream fileInputStream, //
			@FormDataParam("file") final FormDataContentDisposition fileMetaData //
	) {
		try (DBAccess db = DBAccess.createInterface()) {
			// correct input string stream :
			trackId = DataTools.multipartCorrection(trackId);
			albumId = DataTools.multipartCorrection(albumId);
			artistId = DataTools.multipartCorrection(artistId);
			genderId = DataTools.multipartCorrection(genderId);
			title = DataTools.multipartCorrection(title);

			// public NodeSmall uploadFile(final FormDataMultiPart form) {
			LOGGER.info("Upload media file: " + fileMetaData);
			LOGGER.info("    > genderId: " + genderId);
			LOGGER.info("    > artistId: " + artistId);
			LOGGER.info("    > albumId: " + albumId);
			LOGGER.info("    > trackId: " + trackId);
			LOGGER.info("    > title: " + title);
			LOGGER.info("    > fileInputStream: " + fileInputStream);
			LOGGER.info("    > fileMetaData: " + fileMetaData);
			/* if (typeId == null) { return Response.status(406). entity("Missong Input 'type'"). type("text/plain"). build(); } */

			final long tmpUID = DataTools.getTmpDataId();
			final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);
			Data data = DataTools.getWithSha512(db, sha512);
			if (data == null) {
				LOGGER.info("Need to add the data in the BDD ... ");

				try {
					data = DataTools.createNewData(db, tmpUID, fileMetaData.getFileName(), sha512);
				} catch (final IOException ex) {
					DataTools.removeTemporaryFile(tmpUID);
					ex.printStackTrace();
					return Response.notModified("can not create input media").build();
				} catch (final SQLException ex) {
					ex.printStackTrace();
					DataTools.removeTemporaryFile(tmpUID);
					return Response.notModified("Error in SQL insertion ...").build();
				}
			} else if (data.deleted) {
				LOGGER.info("Data already exist but deleted");
				DataTools.undelete(db, data.oid);
				data.deleted = false;
			} else {
				LOGGER.info("Data already exist ... all good");
			}
			LOGGER.info("add media");

			Track trackElem = new Track();
			trackElem.name = title;
			trackElem.track = trackId != null ? Long.parseLong(trackId) : null;
			trackElem.albumId = albumId != null ? Long.parseLong(albumId) : null;
			trackElem.genderId = genderId != null ? Long.parseLong(genderId) : null;
			trackElem.dataId = data.oid;
			// Now list of artist has an internal management:
			if (artistId != null) {
				trackElem.artists = new ArrayList<>();
				trackElem.artists.add(artistId != null ? Long.parseLong(artistId) : null);
			}
			trackElem = DataAccess.insert(trackElem, new CheckFunction(CHECKER));
			/* Old mode of artist insertion (removed due to the slowlest request of getting value if (artistElem != null) { this.dam.addLink(Track.class, trackElem.id, "artist", artistElem.id); } */
			return Response.ok(trackElem).build();
		} catch (final Exception ex) {
			LOGGER.info("Catch an unexpected error ... {}", ex.getMessage());
			ex.printStackTrace();
			return Response.status(417).entity("Back-end error : " + ex.getMessage()).type("text/plain").build();
		}
	}

}
