package com.ffii.lioner.modules.lioner.web;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.ffii.lioner.modules.lioner.entity.File;
import com.ffii.lioner.modules.lioner.entity.FileBlob;
import com.ffii.lioner.modules.lioner.entity.FileRef;
import com.ffii.lioner.modules.lioner.service.FileBlobService;
import com.ffii.lioner.modules.lioner.service.FileRefService;
import com.ffii.lioner.modules.lioner.service.FileService;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.utils.Params;

@RestController
@RequestMapping("/file")
public class FileController {

	private final Log logger = LogFactory.getLog(getClass());
	private FileService fileService;
	private FileRefService fileRefService;
	private FileBlobService fileBlobService;

	public FileController(FileService fileService, FileRefService fileRefService, FileBlobService fileBlobService) {
		this.fileService = fileService;
		this.fileBlobService = fileBlobService;
		this.fileRefService = fileRefService;
	}

	@PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Map<String, Object> saveOrUpdate(@RequestParam String eventName, @RequestParam MultipartFile[] files) {
		//logger.info(files);
		return Map.of(Params.DATA, " fileService.saveOrUpdate(req)");
	}

	@DeleteMapping("/{id}")
	public Map<String,Object> delete(@PathVariable Long id) {
		return fileService.deleteFile(id);
	}

	@PostMapping(value = "/thumbnail/ul", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Map<String, Object> saveOrUpdate(
        @RequestParam Long fileId,
        @RequestParam(value = "remarks", required = false) String remarks,
        @RequestParam(value = "file", required = false) MultipartFile file)
		 throws Exception {

		return Map.of(Params.DATA, fileService.updateFileDisplay(fileId, file, remarks));
	}

	@RequestMapping(value = "/dl/{id}/{skey}/{filename}", method = RequestMethod.GET)
	public ResponseEntity<Resource> download(@RequestParam(defaultValue = "false") boolean dl,
			@PathVariable Long id, @PathVariable String skey) throws Exception {

		File file = fileService.findFileByIdAndKey(id, skey)
				.orElseThrow(NotFoundException::new);

		return ResponseEntity.ok()
				.cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS))
				.header("filename", file.getFilename())
				.body(new ByteArrayResource(fileService.getfileBlob(file.getId())));
				//.body(new ByteArrayResource(unzippedData));
	}

	@RequestMapping(value ="/thumbnail/{id}/{skey}/{filename}",  method = RequestMethod.GET)
	public ResponseEntity<Resource> thumbnail(
			@RequestParam(defaultValue = "false") boolean dl,
			@PathVariable Long id,
			@PathVariable String skey,
			@PathVariable String filename) throws IOException {

		File quickRef = fileService.loadFileOnly(id,skey,filename);

		if(quickRef.getMimetype().equals("application/x-7z-compressed") ||
			quickRef.getMimetype().equals("application/x-rar-compressed") ||
			quickRef.getMimetype().equals("application/zip")
		){
			return null;
		}

		if(quickRef.getMimetype().equals("video/mpeg") ||
			quickRef.getMimetype().equals("video/mp4") ||
			quickRef.getMimetype().equals("video/x-matroska") ||
			quickRef.getMimetype().equals("video/quicktime") ||
			quickRef.getMimetype().equals("video/x-msvideo")
		){
			FileRef fileRef = fileRefService.findByFileId(quickRef.getId()).get();
			byte[] thumbnailBlob = fileService.getfileBlobById(fileRef.getThumbnailFileId());
			//byte[] thumbnailBlob = processVideoAndGetThumbnail(unzippedData);

				// Create an appropriate HTTP response with the thumbnail blob
				HttpHeaders headers = new HttpHeaders();
				headers.setContentType(MediaType.IMAGE_JPEG);

			return ResponseEntity.ok()
			.header("filename", quickRef.getFilename())
			.body(new ByteArrayResource(thumbnailBlob));
		}

		List<String> typeToGenerateThumbnail = List.of(
			"image/jpeg",
			"image/png",
			"application/pdf"
		);

		if(typeToGenerateThumbnail.contains(quickRef.getMimetype())){
			Pair<File, byte[]> pair = fileService.loadFile(id, skey, filename);
			if (pair.getLeft() != null && pair.getRight() != null) {
				File file = pair.getLeft();
				byte[] fileByte = pair.getRight();

				if (file.getMimetype().equals("image/jpeg") 
					|| file.getMimetype().equals("image/png")
					) {
					int limit = 500;
					BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileByte));
					//BufferedImage image = ImageIO.read(new ByteArrayInputStream(unzippedData));
					int width = image.getWidth();
					int height = image.getHeight();
					if (width > height) {
						if (width > limit) {
							height = height * limit / width;
							width = limit;
						}
					} else {
						if (height > limit) {
							width = width * limit / height;
							height = limit;
						}
					}
					image = scale(image, width, height);
					ByteArrayOutputStream tmp = new ByteArrayOutputStream();
					ImageIO.write(image, "jpg", tmp);
					tmp.close();

					return ResponseEntity.ok()
					.cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS))
					.header("filename", file.getFilename())
					.body(new ByteArrayResource(tmp.toByteArray()));
				}

				if(file.getMimetype().equals("application/pdf")
				){
					BufferedImage thumbnailPdf = generateThumbnailFromPdf(fileByte);
					//BufferedImage thumbnailPdf = generateThumbnailFromPdf(unzippedData);
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					ImageIO.write(thumbnailPdf, "jpg", baos);
					byte[] thumbnailBytes = baos.toByteArray();
						// Create an appropriate HTTP response with the thumbnail blob
						HttpHeaders headers = new HttpHeaders();
						headers.setContentType(MediaType.IMAGE_JPEG);

					return ResponseEntity.ok()
					.cacheControl(CacheControl.maxAge(10, TimeUnit.DAYS))
					.header("filename", file.getFilename())
					.body(new ByteArrayResource(thumbnailBytes));
				}
			}
		}
		else{
			return null;
		}
		
		return null;
	}

	private BufferedImage generateThumbnailFromPdf(byte[] pdfBytes) throws IOException {
        // Load the PDF document from the byte array
        try (PDDocument document = Loader.loadPDF(pdfBytes)) {
            // Create a PDF renderer
            PDFRenderer pdfRenderer = new PDFRenderer(document);

            // Render the first page of the PDF to an image
            BufferedImage pageImage = pdfRenderer.renderImageWithDPI(0, 300); // Adjust DPI as needed

            // Generate a thumbnail image by scaling down the page image
            int thumbnailWidth = 200; // Adjust the thumbnail width as needed
            int thumbnailHeight = (int) ((double) thumbnailWidth / pageImage.getWidth() * pageImage.getHeight());
            BufferedImage thumbnailImage = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB);
            thumbnailImage.createGraphics().drawImage(pageImage.getScaledInstance(thumbnailWidth, thumbnailHeight, BufferedImage.SCALE_SMOOTH), 0, 0, null);

            return thumbnailImage;
        }
    }
	

	static BufferedImage scale(BufferedImage originalImage, int w, int h) {
		BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		int x, y;
		int ww = originalImage.getWidth();
		int hh = originalImage.getHeight();
		for (x = 0; x < w; x++) {
			for (y = 0; y < h; y++) {
				int col = originalImage.getRGB(x * ww / w, y * hh / h);
				img.setRGB(x, y, col);
			}
		}
		return img;
	}
}
