package com.ffii.lioner.modules.lioner.pdf.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Optional;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.springframework.stereotype.Service;

import com.ffii.lioner.modules.lioner.pdf.entity.FormSigPage;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;

@Service
public class PdfMergeService {

    private final FormSigPageService formSigPageService;

    public PdfMergeService(FormSigPageService formSigPageService) {
        this.formSigPageService = formSigPageService;
    }

    // --- ACROFORM REMOVAL HELPER METHOD ---

    /**
     * Flattens form fields, preserving their appearance (text/checkmarks)
     * but making the PDF plain and non-interactive.
     */
    private void flattenPdf(PdfDocument doc) {
        PdfAcroForm acroForm = PdfAcroForm.getAcroForm(doc, true);
        acroForm.flattenFields();
    }

    public void removePdfPassword() throws IOException {
        String filePathB = "C:\\dev\\pdf\\pdfB.pdf";
        File fileB = new File(filePathB);
        String outputPath = "C:\\dev\\pdf\\pdfOut.pdf";

        try (
            PDDocument pdfB = Loader.loadPDF(fileB);
            PDDocument mergedPdf = new PDDocument()) {

            for (int i = 0; i < pdfB.getNumberOfPages(); i++) {
                PDPage page = pdfB.getPage(i);
                mergedPdf.addPage(page);
            }
            pdfB.setAllSecurityToBeRemoved(true);
            pdfB.save(new File(outputPath));
            mergedPdf.save(new File(outputPath));
            mergedPdf.close();
        }
    }

    public void mergePdfs() throws IOException {
        String filePathA = "C:\\dev\\pdf\\pdfA.pdf";
        File fileA = new File(filePathA);
        String filePathB = "C:\\dev\\pdf\\pdfB.pdf";
        File fileB = new File(filePathB);
        String outputPath = "C:\\dev\\pdf\\pdfOut.pdf";

        try (
            PDDocument pdfA = Loader.loadPDF(fileA);
            PDDocument pdfB = Loader.loadPDF(fileB);
            PDDocument mergedPdf = new PDDocument()) {

            for (int i = 0; i < pdfB.getNumberOfPages(); i++) {
                PDPage page = pdfB.getPage(i);
                mergedPdf.addPage(page);
            }
            mergedPdf.save(new File(outputPath));
        }
    }

    public byte[] flatPdfsItext7(byte[] pdfABytes) throws IOException {
        byte[] pdfAFlattenedBytes;
        try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes));
             ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream();
             PdfWriter tempWriterA = new PdfWriter(tempBaosA);
             PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) {

            flattenPdf(docA_mod);
            docA_mod.close();
            pdfAFlattenedBytes = tempBaosA.toByteArray();
            return tempBaosA.toByteArray();
        }
    }

    /**
     * Merge upload1 (signature) PDF using config from form_sig_page. Uses asOfDate for versioning.
     */
    public byte[] mergePdfsItext7(String formCode, LocalDate asOfDate, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        Optional<FormSigPage> configOpt = formSigPageService.getUpload1Config(formCode, asOfDate);

        byte[] pdfAFlattenedBytes;
        try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes));
             ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream();
             PdfWriter tempWriterA = new PdfWriter(tempBaosA);
             PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) {

            flattenPdf(docA_mod);
            docA_mod.close();
            pdfAFlattenedBytes = tempBaosA.toByteArray();
        }

        byte[] pdfBFlattenedBytes = null;
        if (pdfBBytes != null && pdfBBytes.length > 0) {
            try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes));
                 ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream();
                 PdfWriter tempWriterB = new PdfWriter(tempBaosB);
                 PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) {

                flattenPdf(docB_mod);
                docB_mod.close();
                pdfBFlattenedBytes = tempBaosB.toByteArray();
            }
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             PdfWriter writer = new PdfWriter(baos);
             PdfDocument mergedPdf = new PdfDocument(writer);
             PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes));
             PdfDocument docA = new PdfDocument(readerA_merge)) {

            PdfMerger merger = new PdfMerger(mergedPdf);
            int totalPagesA = docA.getNumberOfPages();

            if (configOpt.isEmpty()) {
                merger.merge(docA, 1, totalPagesA);
                mergedPdf.close();
                return baos.toByteArray();
            }

            FormSigPage cfg = configOpt.get();
            int pageFrom = cfg.getPageFrom();
            int pageTo = cfg.getPageTo();
            if (totalPagesA >= pageTo) {
                int repStartA = pageFrom;
                int repEndA = pageTo;
                int pagesToInsertB = pageTo - pageFrom + 1;
                if (repStartA > 1) {
                    merger.merge(docA, 1, repStartA - 1);
                }
                if (pdfBFlattenedBytes != null) {
                    try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes));
                         PdfDocument docB = new PdfDocument(readerB_merge)) {
                        if (docB.getNumberOfPages() >= pagesToInsertB) {
                            merger.merge(docB, 1, pagesToInsertB);
                        }
                    }
                }
                if (totalPagesA > repEndA) {
                    merger.merge(docA, repEndA + 1, totalPagesA);
                }
            } else {
                merger.merge(docA, 1, totalPagesA);
            }

            mergedPdf.close();
            return baos.toByteArray();
        }
    }

    /** Backward compatibility: use today as asOfDate. */
    public byte[] mergePdfsItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        return mergePdfsItext7(formCode, LocalDate.now(), pdfABytes, pdfBBytes);
    }

    /**
     * Merge upload2 PDF using config from form_sig_page.
     */
    public byte[] mergePdf2sItext7(String formCode, LocalDate asOfDate, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        Optional<FormSigPage> configOpt = formSigPageService.getUpload2Config(formCode, asOfDate);

        byte[] pdfAFlattenedBytes;
        try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes));
             ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream();
             PdfWriter tempWriterA = new PdfWriter(tempBaosA);
             PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) {

            flattenPdf(docA_mod);
            docA_mod.close();
            pdfAFlattenedBytes = tempBaosA.toByteArray();
        }

        byte[] pdfBFlattenedBytes = null;
        if (pdfBBytes != null && pdfBBytes.length > 0) {
            try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes));
                 ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream();
                 PdfWriter tempWriterB = new PdfWriter(tempBaosB);
                 PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) {

                flattenPdf(docB_mod);
                docB_mod.close();
                pdfBFlattenedBytes = tempBaosB.toByteArray();
            }
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             PdfWriter writer = new PdfWriter(baos);
             PdfDocument mergedPdf = new PdfDocument(writer);
             PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes));
             PdfDocument docA = new PdfDocument(readerA_merge)) {

            PdfMerger merger = new PdfMerger(mergedPdf);
            int totalPagesA = docA.getNumberOfPages();

            if (configOpt.isEmpty()) {
                merger.merge(docA, 1, totalPagesA);
                mergedPdf.close();
                return baos.toByteArray();
            }

            FormSigPage cfg = configOpt.get();
            int repStartA = cfg.getPageFrom();
            int repEndA = cfg.getPageTo();
            int repCountB = repEndA - repStartA + 1;

            if (totalPagesA >= repEndA) {
                if (repStartA > 1) {
                    merger.merge(docA, 1, repStartA - 1);
                }
                if (pdfBFlattenedBytes != null) {
                    try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes));
                         PdfDocument docB = new PdfDocument(readerB_merge)) {
                        if (docB.getNumberOfPages() >= repCountB) {
                            merger.merge(docB, 1, repCountB);
                        }
                    }
                }
                if (totalPagesA > repEndA) {
                    merger.merge(docA, repEndA + 1, totalPagesA);
                }
            } else {
                merger.merge(docA, 1, totalPagesA);
            }

            mergedPdf.close();
            return baos.toByteArray();
        }
    }

    /** Backward compatibility: use today as asOfDate. */
    public byte[] mergePdf2sItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        return mergePdf2sItext7(formCode, LocalDate.now(), pdfABytes, pdfBBytes);
    }

    /**
     * Merge upload3 PDF using config from form_sig_page (third signature block; run after upload1 and upload2).
     */
    public byte[] mergePdf3sItext7(String formCode, LocalDate asOfDate, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        Optional<FormSigPage> configOpt = formSigPageService.getUpload3Config(formCode, asOfDate);

        byte[] pdfAFlattenedBytes;
        try (PdfReader readerA = new PdfReader(new ByteArrayInputStream(pdfABytes));
             ByteArrayOutputStream tempBaosA = new ByteArrayOutputStream();
             PdfWriter tempWriterA = new PdfWriter(tempBaosA);
             PdfDocument docA_mod = new PdfDocument(readerA, tempWriterA)) {

            flattenPdf(docA_mod);
            docA_mod.close();
            pdfAFlattenedBytes = tempBaosA.toByteArray();
        }

        byte[] pdfBFlattenedBytes = null;
        if (pdfBBytes != null && pdfBBytes.length > 0) {
            try (PdfReader readerB = new PdfReader(new ByteArrayInputStream(pdfBBytes));
                 ByteArrayOutputStream tempBaosB = new ByteArrayOutputStream();
                 PdfWriter tempWriterB = new PdfWriter(tempBaosB);
                 PdfDocument docB_mod = new PdfDocument(readerB, tempWriterB)) {

                flattenPdf(docB_mod);
                docB_mod.close();
                pdfBFlattenedBytes = tempBaosB.toByteArray();
            }
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             PdfWriter writer = new PdfWriter(baos);
             PdfDocument mergedPdf = new PdfDocument(writer);
             PdfReader readerA_merge = new PdfReader(new ByteArrayInputStream(pdfAFlattenedBytes));
             PdfDocument docA = new PdfDocument(readerA_merge)) {

            PdfMerger merger = new PdfMerger(mergedPdf);
            int totalPagesA = docA.getNumberOfPages();

            if (configOpt.isEmpty()) {
                merger.merge(docA, 1, totalPagesA);
                mergedPdf.close();
                return baos.toByteArray();
            }

            FormSigPage cfg = configOpt.get();
            int repStartA = cfg.getPageFrom();
            int repEndA = cfg.getPageTo();
            int repCountB = repEndA - repStartA + 1;

            if (totalPagesA >= repEndA) {
                if (repStartA > 1) {
                    merger.merge(docA, 1, repStartA - 1);
                }
                if (pdfBFlattenedBytes != null) {
                    try (PdfReader readerB_merge = new PdfReader(new ByteArrayInputStream(pdfBFlattenedBytes));
                         PdfDocument docB = new PdfDocument(readerB_merge)) {
                        if (docB.getNumberOfPages() >= repCountB) {
                            merger.merge(docB, 1, repCountB);
                        }
                    }
                }
                if (totalPagesA > repEndA) {
                    merger.merge(docA, repEndA + 1, totalPagesA);
                }
            } else {
                merger.merge(docA, 1, totalPagesA);
            }

            mergedPdf.close();
            return baos.toByteArray();
        }
    }

    public byte[] mergePdf3sItext7(String formCode, byte[] pdfABytes, byte[] pdfBBytes) throws IOException {
        return mergePdf3sItext7(formCode, LocalDate.now(), pdfABytes, pdfBBytes);
    }
}
