package stirling.software.SPDF.controller.api.filters;

import java.io.IOException;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;

import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;

import lombok.RequiredArgsConstructor;

import stirling.software.SPDF.config.swagger.FilterResponse;
import stirling.software.SPDF.model.api.PDFComparisonAndCount;
import stirling.software.SPDF.model.api.PDFWithPageNums;
import stirling.software.SPDF.model.api.filter.ContainsTextRequest;
import stirling.software.SPDF.model.api.filter.FileSizeRequest;
import stirling.software.SPDF.model.api.filter.PageRotationRequest;
import stirling.software.SPDF.model.api.filter.PageSizeRequest;
import stirling.software.common.annotations.AutoJobPostMapping;
import stirling.software.common.annotations.api.FilterApi;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.PdfUtils;
import stirling.software.common.util.WebResponseUtils;

@FilterApi
@RequiredArgsConstructor
public class FilterController {

    private final CustomPDFDocumentFactory pdfDocumentFactory;

    @AutoJobPostMapping(
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
            value = "/filter-contains-text")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF contains set text, returns true if does",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> containsText(@ModelAttribute ContainsTextRequest request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        String text = request.getText();
        String pageNumber = request.getPageNumbers();

        try (PDDocument pdfDocument = pdfDocumentFactory.load(inputFile)) {
            if (PdfUtils.hasText(pdfDocument, pageNumber, text)) {
                return WebResponseUtils.pdfDocToWebResponse(
                        pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
            }
        }
        return null;
    }

    // TODO
    @AutoJobPostMapping(
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
            value = "/filter-contains-image")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF contains an image",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> containsImage(@ModelAttribute PDFWithPageNums request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        String pageNumber = request.getPageNumbers();

        PDDocument pdfDocument = pdfDocumentFactory.load(inputFile);
        if (PdfUtils.hasImages(pdfDocument, pageNumber))
            return WebResponseUtils.pdfDocToWebResponse(
                    pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
        return null;
    }

    @AutoJobPostMapping(
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
            value = "/filter-page-count")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF is greater, less or equal to a setPageCount",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> pageCount(@ModelAttribute PDFComparisonAndCount request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        int pageCount = request.getPageCount();
        String comparator = request.getComparator();
        // Load the PDF
        PDDocument document = pdfDocumentFactory.load(inputFile);
        int actualPageCount = document.getNumberOfPages();
        // Perform the comparison
        boolean valid =
                switch (comparator) {
                    case "Greater" -> actualPageCount > pageCount;
                    case "Equal" -> actualPageCount == pageCount;
                    case "Less" -> actualPageCount < pageCount;
                    default ->
                            throw ExceptionUtils.createInvalidArgumentException(
                                    "comparator", comparator);
                };

        if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
        return null;
    }

    @AutoJobPostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-page-size")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF is of a certain size",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> pageSize(@ModelAttribute PageSizeRequest request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        String standardPageSize = request.getStandardPageSize();
        String comparator = request.getComparator();

        // Load the PDF
        PDDocument document = pdfDocumentFactory.load(inputFile);

        PDPage firstPage = document.getPage(0);
        PDRectangle actualPageSize = firstPage.getMediaBox();

        // Calculate the area of the actual page size
        float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();

        // Get the standard size and calculate its area
        PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
        float standardArea = standardSize.getWidth() * standardSize.getHeight();

        // Perform the comparison
        boolean valid =
                switch (comparator) {
                    case "Greater" -> actualArea > standardArea;
                    case "Equal" -> actualArea == standardArea;
                    case "Less" -> actualArea < standardArea;
                    default ->
                            throw ExceptionUtils.createInvalidArgumentException(
                                    "comparator", comparator);
                };

        if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
        return null;
    }

    @AutoJobPostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/filter-file-size")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF is a set file size",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> fileSize(@ModelAttribute FileSizeRequest request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        long fileSize = request.getFileSize();
        String comparator = request.getComparator();

        // Get the file size
        long actualFileSize = inputFile.getSize();

        // Perform the comparison
        boolean valid =
                switch (comparator) {
                    case "Greater" -> actualFileSize > fileSize;
                    case "Equal" -> actualFileSize == fileSize;
                    case "Less" -> actualFileSize < fileSize;
                    default ->
                            throw ExceptionUtils.createInvalidArgumentException(
                                    "comparator", comparator);
                };

        if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
        return null;
    }

    @AutoJobPostMapping(
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
            value = "/filter-page-rotation")
    @FilterResponse
    @Operation(
            summary = "Checks if a PDF is of a certain rotation",
            description = "Input:PDF Output:Boolean Type:SISO")
    public ResponseEntity<byte[]> pageRotation(@ModelAttribute PageRotationRequest request)
            throws IOException, InterruptedException {
        MultipartFile inputFile = request.getFileInput();
        int rotation = request.getRotation();
        String comparator = request.getComparator();

        // Load the PDF
        PDDocument document = pdfDocumentFactory.load(inputFile);

        // Get the rotation of the first page
        PDPage firstPage = document.getPage(0);
        int actualRotation = firstPage.getRotation();

        // Perform the comparison
        boolean valid =
                switch (comparator) {
                    case "Greater" -> actualRotation > rotation;
                    case "Equal" -> actualRotation == rotation;
                    case "Less" -> actualRotation < rotation;
                    default ->
                            throw ExceptionUtils.createInvalidArgumentException(
                                    "comparator", comparator);
                };

        if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
        return null;
    }
}
