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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
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.commonField.entity.CommonField;
import com.ffii.lioner.modules.lioner.commonField.service.CommonFieldService;
import com.ffii.lioner.modules.lioner.pdf.entity.Pdf;
import com.ffii.lioner.modules.lioner.pdf.service.PdfService;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;


@RestController
@RequestMapping("/pdf2")
@CrossOrigin(origins = "", allowedHeaders = "")

public class Pdf2Controller {

    private final String UPLOAD_DIR = "uploads/"; // Configure this path
    private static final Logger logger = LoggerFactory.getLogger(Pdf2Controller.class);

    @Autowired
    private CommonFieldService commonFieldService;

    @Autowired
    private PdfService pdfService;
	
     @GetMapping("/get")
    public ResponseEntity<Resource> get(@RequestParam long id, @RequestParam long clientId) {
        logger.info("Fetching PDF for id: " + id);

        Map<String, Object> pdfData = null;
        try {
            pdfData = pdfService.getPdfBlob(id);
        } catch (Exception e) {
            logger.error("Error calling pdfService.getPdfBlob for ID: " + id, e);
            return ResponseEntity.internalServerError().body(null);
        }

        if (pdfData == null || !pdfData.containsKey("blobValue")) {
            logger.warn("PDF data or 'blobValue' missing for ID: " + id);
            return ResponseEntity.notFound().build();
        }

        Object blobObject = pdfData.get("blobValue");
        if (!(blobObject instanceof byte[])) {
            logger.error("Value for 'blobValue' is not a byte array for ID: " + id + ". Actual type: " + (blobObject != null ? blobObject.getClass().getName() : "null"));
            return ResponseEntity.internalServerError().body(null);
        }

        byte[] pdfBytes = (byte[]) blobObject;
        logger.info("Original PDF byte array length: " + pdfBytes.length);

        byte[] modifiedPdfBytes;
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pdfBytes);
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            PdfReader reader = new PdfReader(inputStream);
            PdfWriter writer = new PdfWriter(outputStream);
            PdfDocument pdfDoc = new PdfDocument(reader, writer);
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);

            if (form != null) {
                // --- START DEBUGGING CODE TO LIST ALL PDF FORM FIELDS ---
                logger.info("--- Listing all form fields in the PDF (for ID: {}) ---", id);
                if (form.getFormFields().isEmpty()) {
                    logger.info("No form fields found in this PDF.");
                } else {
                    form.getFormFields().forEach((fieldName, pdfFormField) -> {
                        logger.info("  - Field Name: '{}', Type: '{}'", fieldName, pdfFormField.getFormType());
                    });
                }
                logger.info("--- End of form fields list ---");
                // --- END DEBUGGING CODE ---

                // Load common field data
                Long commonFieldId = commonFieldService.getByClientId(clientId);
                logger.info("id? " + id);
                CommonField commonField = commonFieldService.find(commonFieldId).orElse(null);

                if(commonField == null){
                    logger.info("No common field data found for clientId: " + clientId);
                    // do nothing
                } else {
                    // It's highly recommended to check if the field exists before setting its value
                    // This prevents NullPointerExceptions if a field is missing in a template

                    if(id == 1){ // This is IDA
                        logger.info("Processing IDA form (ID: 1)");
                        setValueIfPresent(form, "fill_6", commonField.getName(), "commonField.getName()");
                        setValueIfPresent(form, "fill_7", commonField.getNameChi(), "commonField.getNameChi()");
                        setValueIfPresent(form, "fill_11", commonField.getIdCard(), "commonField.getIdCard()");

                    } else if(id == 2){ // This is FNA
                        logger.info("Processing FNA form (ID: 2)");
                        setValueIfPresent(form, "fna_a_ name", commonField.getName(), "commonField.getName()");
                        setValueIfPresent(form, "fna_a_birth", commonField.getDateOfBirth(), "commonField.getDateOfBirth()");
                        setValueIfPresent(form, "fna_a_occupation", commonField.getOccupation(), "commonField.getOccupation()");

                    } else if(id == 3){ // This is HSBCFIN
                        logger.info("Processing HSBCFIN form (ID: 3)");
                        setValueIfPresent(form, "fill_3", commonField.getName(), "commonField.getName()");
                        setValueIfPresent(form, "fill_4", commonField.getNameChi(), "commonField.getNameChi()");
                        setValueIfPresent(form, "fill_5", commonField.getGender(), "commonField.getGender()");
                        setValueIfPresent(form, "fill_6", commonField.getDateOfBirth(), "commonField.getDateOfBirth()"); // Note: fill_6 used here again
                        setValueIfPresent(form, "fill_9", commonField.getOccupation(), "commonField.getOccupation()");
                    }

                    // These fields are set unconditionally if commonField is not null
                    // Ensure these fields exist across all your templates or handle them conditionally
                    setValueIfPresent(form, "address", "123 Main St, Anytown, USA", "hardcoded address");
                    setValueIfPresent(form, "dateOfBirth", "01/01/1990", "hardcoded dateOfBirth");
                }

                //form.flattenFields(); // Flatten fields after setting all values

            } else {
                logger.warn("No AcroForm found in PDF for ID: " + id + ". PDF might not have fillable fields.");
            }

            pdfDoc.close();
            modifiedPdfBytes = outputStream.toByteArray();
            logger.info("Modified PDF byte array length: " + modifiedPdfBytes.length);

        } catch (IOException e) {
            logger.error("Error processing PDF with iText for ID: " + id, e);
            return ResponseEntity.internalServerError().body(null);
        }

        Resource resource = new ByteArrayResource(modifiedPdfBytes);
        String filename = (String) pdfData.getOrDefault("fileName", "document_" + id + ".pdf");

        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\"")
            .contentType(MediaType.APPLICATION_PDF) // Corrected: no charset for binary PDFs
            .body(resource);
    }

    // Helper method to set field value safely
    private void setValueIfPresent(PdfAcroForm form, String fieldName, String value, String valueSource) {
        PdfFormField field = form.getField(fieldName);
        if (field != null) {
            if (value != null) {
                try {
                    field.setValue(value);
                    logger.info("Set field '{}' to value '{}' (from {})", fieldName, value, valueSource);
                } catch (Exception e) {
                    logger.error("Error setting value for field '{}' with value '{}' (from {}): {}", fieldName, value, valueSource, e.getMessage());
                }
            } else {
                logger.warn("Value for field '{}' (from {}) is null. Skipping setting value.", fieldName, valueSource);
            }
        } else {
            logger.warn("PDF field '{}' not found in the document. Skipping setting value.", fieldName);
        }
    }
    
    //no use
    // Endpoint to download the blank AcroForm
    @GetMapping("/download/template")
    public ResponseEntity<Resource> downloadPdfTemplate(@RequestParam String formCode) throws IOException {
        logger.info("download/template formCode:" + formCode);
        //BAD EXAMPLE
        Path filePath = null; // Paths.get("src/main/resources/template/pdf/template_form.pdf").normalize(); // Path to your blank AcroForm
        switch (formCode) {
                            case "HSBCFIN" -> filePath = Paths.get("../../../../../resources/templates/pdf/HSBCFIN.pdf").normalize(); 
                            case "IDA" -> filePath = Paths.get("./IDA232.pdf").normalize(); 
                            case "FNA" -> filePath = Paths.get("../../../../../resources/templates/pdf/FNA.pdf").normalize(); 
                            
                        };

        
        Resource resource = new UrlResource(filePath.toUri());

        logger.info("File Found: "  + (resource != null));

        if (resource.exists()) {
            return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_PDF)
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                    .body(resource);
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    // Endpoint to upload the edited PDF
    @PostMapping("/upload")
    public ResponseEntity<String> uploadEditedPdf(@RequestParam("file") MultipartFile file, @RequestParam Long clientId, @RequestParam String formCode) {
        try {
            // Ensure upload directory exists
            Path uploadPath = Paths.get(UPLOAD_DIR).toAbsolutePath().normalize();
            Files.createDirectories(uploadPath);

            String fileName = UUID.randomUUID().toString() + "-" + StringUtils.cleanPath(file.getOriginalFilename());
            Path targetLocation = uploadPath.resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

            // --- Process the uploaded PDF (e.g., extract form data, save to DB) ---
            //PDDocument document = PDDocument.load(targetLocation.toFile());
            PDDocument document = Loader.loadPDF(targetLocation.toFile()); // Changed!
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

            System.out.println("formCode: " + formCode + " clientId:" + clientId);
            if (acroForm != null) {
                // Example: Iterate through fields and print values
                for (PDField field : acroForm.getFields()) {
                    if("HSBCFIN".equals(formCode)){
                        System.out.println("HSBCFIN ");
                        //find the commonField by cleintId , CommonField commonField = 
                    }
                    
                    System.out.println("Field: " + field.getPartialName() + ", Value: " + field.getValueAsString());
                    // You can save this data to a database, or perform other operations
                }
            }

            document.close();
            // -------------------------------------------------------------------

            return ResponseEntity.ok("File uploaded and processed successfully: " + fileName);
        } catch (IOException ex) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Could not upload or process file: " + ex.getMessage());
        }
    }

    //no use
    // Optional: Endpoint to upload only the form data (FDF/XFDF)
    @PostMapping("/upload/data")
    public ResponseEntity<String> uploadFormData(@RequestBody Map<String, String> formData) {
        // This approach requires the frontend to extract form data and send it as JSON
        // On the backend, you'd then use this data to populate the original PDF template
        // and save a new PDF or just the data in your database.
        // This is often more efficient than re-uploading the entire PDF.

        // Example: Logic to process form data
        System.out.println("Received form data: " + formData);
        // You would then load your template PDF, fill it with this data, and save.
        return ResponseEntity.ok("Form data received and processed.");
    }

    @PostMapping("/save-pdf")
    public ResponseEntity<String> savePdf(@RequestBody byte[] pdfContent, @RequestParam long clientId, @RequestParam String formCode) {
        System.out.println("formCode0000: " + formCode + " clientId:" + clientId);
        logger.info("formCode234d: " + formCode + " clientId:" + clientId);
        try {
            String fileName = "edited-" + System.currentTimeMillis() + ".pdf";
            //System.out.println("fileName0: " + fileName);
            File file = new File(UPLOAD_DIR + fileName);
            logger.info("fileName: " + fileName);
            try (FileOutputStream fos = new FileOutputStream(file)) {
                IOUtils.write(pdfContent, fos);
            }

            logger.info("fileName2: " + fileName);
            //PDDocument document = PDDocument.load(targetLocation.toFile());
            PDDocument document = Loader.loadPDF(file); // Changed!
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

            logger.info("acroForm? " + (acroForm != null) );
            if (acroForm != null) {
                //create CommonField if not exists for this client Id
                Long id = commonFieldService.getByClientId(clientId);
                logger.info("id? " + id);
                CommonField commonField = null;
                if(id > 0){
                    commonField  = commonFieldService.find(id).orElse(new CommonField());
                }else{
                    commonField = new CommonField();
                }
                //logger.info("commonField? " + commonField.toString());
                commonField.setClientId(clientId);

                logger.info("commonField? " + commonField.getId());
                // Example: Iterate through fields and print values
                for (PDField field : acroForm.getFields()) {
                    String fieldName = field.getPartialName();
                    String fieldValue = field.getValueAsString();

                    if("IDA".equals(formCode)){
                        logger.info("IDA ");

                        switch (fieldName) {
                            case "fill_6" -> commonField.setName(fieldValue);
                            case "fill_7" -> commonField.setNameChi(fieldValue);
                            case "fill_11" -> commonField.setIdCard(fieldValue);
                           
                        };
                    }

                    if("IDA".equals(formCode)){
                        logger.info("IDA ");

                        switch (fieldName) {
                            case "fna_a_ name" -> commonField.setName(fieldValue);
                            case "fna_a_birth" -> commonField.setDateOfBirth(fieldValue);
                            case "fna_a_occupation" -> commonField.setOccupation(fieldValue);
                        };
                    }

                    if("HSBCFIN".equals(formCode)){
                        logger.info("HSBCFIN ");
                        
                        switch (fieldName) {
                            case "fill_3" -> commonField.setName(fieldValue);
                            case "fill_4" -> commonField.setNameChi(fieldValue);
                            case "fill_5" -> commonField.setGender(fieldValue);
                            case "fill_6" -> commonField.setDateOfBirth(fieldValue);

                            case "fill_9" -> commonField.setOccupation(fieldValue);
                        };
                    }
                    
                    logger.info("end Field: " + field.getPartialName() + ", Value: " + field.getValueAsString());
                    // You can save this data to a database, or perform other operations
                }

                commonFieldService.save(commonField);
            }
            logger.info("ended");
            document.close(); 
            logger.info("closed");   

            return ResponseEntity.ok("PDF saved successfully at " + file.getAbsolutePath());
        } catch (IOException e) {
            return ResponseEntity.status(500).body("Error saving PDF: " + e.getMessage());
        }    
    }

    public void saveCmField(File file, Pdf record) throws IOException {
        // byte[] pdfBytes = Files.readAllBytes(file.toPath());

        // try (FileOutputStream fos = new FileOutputStream(file)) {
        //     IOUtils.write(pdfBytes, fos);
        // }

        //PDDocument document = PDDocument.load(targetLocation.toFile());
        PDDocument document = Loader.loadPDF(file); // Changed!
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

        logger.info("acroForm? " + (acroForm != null) );
        if (acroForm != null) {
            //create CommonField if not exists for this client Id
            Long id = commonFieldService.getByClientId(record.getClientId());
            logger.info("id? " + id);
            CommonField commonField = null;
            if(id > 0){
                commonField  = commonFieldService.find(id).orElse(new CommonField());
            }else{
                commonField = new CommonField();
            }
            //logger.info("commonField? " + commonField.toString());
            commonField.setClientId(record.getClientId());

            logger.info("commonField? " + commonField.getId());
            // Example: Iterate through fields and print values
            for (PDField field : acroForm.getFields()) {
                String fieldName = field.getPartialName();
                String fieldValue = field.getValueAsString();

                if (record.getTemplateId().intValue() == 1) {
                    logger.info("IDA ");

                    switch (fieldName) {
                        case "fill_6" -> commonField.setName(fieldValue);
                        case "fill_7" -> commonField.setNameChi(fieldValue);
                        case "fill_11" -> commonField.setIdCard(fieldValue);
                       
                    };
                }

                if(record.getTemplateId().intValue() == 2){
                    logger.info("FNA ");

                    switch (fieldName) {
                        case "fna_a_ name" -> commonField.setName(fieldValue);
                        case "fna_a_birth" -> commonField.setDateOfBirth(fieldValue);
                        case "fna_a_occupation" -> commonField.setOccupation(fieldValue);
                    };
                }

                if(record.getTemplateId().intValue() == 3){
                    logger.info("HSBCFIN ");
                    
                    switch (fieldName) {
                        case "fill_3" -> commonField.setName(fieldValue);
                        case "fill_4" -> commonField.setNameChi(fieldValue);
                        case "fill_5" -> commonField.setGender(fieldValue);
                        case "fill_6" -> commonField.setDateOfBirth(fieldValue);

                        case "fill_9" -> commonField.setOccupation(fieldValue);
                    };
                }
                
                logger.info("end Field: " + field.getPartialName() + ", Value: " + field.getValueAsString());
                // You can save this data to a database, or perform other operations
            }

            commonFieldService.save(commonField);
        }
        logger.info("ended");
        document.close(); 
        logger.info("closed");
    }
}
