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

import java.io.IOException;
import java.io.InputStream;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;

import com.ffii.lioner.modules.lioner.pdf.entity.Pdf;
import com.ffii.lioner.modules.lioner.pdf.entity.PdfRepository;
import com.ffii.lioner.modules.lioner.entity.ImpEvent;
import com.ffii.lioner.modules.lioner.pdf.req.UpdatePdfReq;
import com.ffii.lioner.modules.lioner.service.FileService;
import com.ffii.lioner.modules.common.SecurityUtils;
import com.ffii.lioner.modules.common.service.AuditLogService;
import com.ffii.lioner.modules.master.entity.SubDivision;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.exception.UnprocessableEntityException;
import com.ffii.core.support.AbstractBaseEntityService;
import com.ffii.core.support.JdbcDao;
import com.ffii.core.utils.BeanUtils;
import com.ffii.core.utils.JsonUtils;
import com.ffii.core.utils.Params;

import jakarta.persistence.Table;

@Service
public class PdfService extends AbstractBaseEntityService<Pdf, Long, PdfRepository> {

	private String pdf_path = "templates/pdf/"; 
    
	private AuditLogService auditLogService;
	private FileService fileService;
	
	public PdfService(JdbcDao jdbcDao, PdfRepository repository, AuditLogService auditLogService, FileService fileService) {
		super(jdbcDao, repository);
		this.auditLogService = auditLogService;
		this.fileService = fileService;
	}

	// public Map<String,Object> getAuditLogObject(Map<String,Object> req){
	// 	StringBuilder sql = new StringBuilder("SELECT"
	// 		+ " e.id,    "
	// 		+ " e.created,    "
	// 		+ " e.createdBy AS createdById,    "
	// 		+ " u1.name AS createdBy,    "
	// 		+ " e.version,    "
	// 		+ " e.modified,    "
	// 		+ " e.modifiedBy AS modifiedById,    "
	// 		+ " e.deleted, "
	// 		+ " u2.name AS modifiedBy,    "
	// 		+ " e.name, "
	// 		+ " e.nameCht, "
	// 		+ " e.description, "
	// 		+ " e.region, "
	// 		+ " e.organization, "
	// 		+ " e.eventType, "
	// 		+ " e.frequency, "
	// 		+ " e.series, "
	// 		+ " e.startDate, "
	// 		+ " e.eventFrom, "
	// 		+ " e.eventTo, "
	// 		+ " e.applicationDeadline, "
	// 		+ " e.nextApplicationDate, "
	// 		+ " e.announcementDate, "
	// 		+ " e.awardDate, "
	// 		+ " e.reminderFlag, "
	// 		+ " e.reminderThreshold, "
	// 		+ " e.reminderInterval, "
	// 		+ " e.reminderLimit  "
	// 		+ " FROM event e 	   "
	// 		+ " LEFT JOIN `user` u1 ON u1.id = e.createdBy     "
	// 		+ " LEFT JOIN `user` u2 ON u2.id = e.modifiedBy       "
	// 		+ " WHERE e.id = :id    "
	// 	);
	// 	return jdbcDao.queryForMap(sql.toString(), req).get();
	// }

	public Map<String, Object> saveOrUpdate(String recordJson, MultipartFile file) {
		ObjectMapper objectMapper = new ObjectMapper();
        UpdatePdfReq req;
        try {
            req = objectMapper.readValue(recordJson, UpdatePdfReq.class);
        } catch (Exception e) {
            throw new UnprocessableEntityException("Invalid JSON format");
        }
		
		Pdf instance;
		// List<SubDivision> onUsingIdList = new ArrayList<SubDivision>();
		if(req.getId()>0){
			instance = find(req.getId()).get();
			fileService.deleteFile(instance.getFileId());	// Delete previous record - WIP
			// onUsingIdList = this.getSelectedSubDivisionList(req.getId());
		}
		else{
			instance = new Pdf();
		}
		System.out.println(req.getTemplateId());
		BeanUtils.copyProperties(req, instance);

		Map<String, Object> pdf = new HashMap<>();
		Map<String, Object> response = new HashMap<>();

		if (file.isEmpty()) {
			throw new UnprocessableEntityException("No file uploaded.");
            // return ResponseEntity.badRequest().body(Map.of("message", "No file uploaded."));
        }
        try {
            // Convert the MultipartFile to a byte array
            byte[] pdfData = file.getBytes();

            MultipartFile[] files = new MultipartFile[1];
            files[0] = file;

            try {
                pdf = fileService.saveOrUpdate(files, (long) 0, "pdf");
            } catch (Exception e) {
                System.out.println(e);
            }

            // Save the PDF data to the database
            // File pdfForm = new PdfForm();
            // pdfForm.setPdfData(pdfData);
            // pdfFormRepository.save(pdfForm); // Assuming you have autowired this repository

            response.put("message", "PDF saved successfully.");
            // response.put("id", pdfForm.getId().toString()); // Return the ID of the saved PDF

        } catch (IOException e) {
			throw new UnprocessableEntityException(
				"Failed to save PDF: "
				+ e
			);
            // e.printStackTrace();
            // return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            //                      .body(Map.of("message", "Failed to save PDF: " + e.getMessage()));
        }
		
		Object fileIdList = pdf.get("fileIdList");
		List<?> list = (List<?>) fileIdList;
		Long fileId = (long) list.get(0);

		instance.setFileId(fileId);
		instance = save(instance);

		response = Map.of(
			"id", instance.getId()
		);

		return response;
	}

	public byte[] getTemplateForm() {
		// Ensure the template PDF is in src/main/resources/template/pdf
        ClassPathResource pdfFile = new ClassPathResource(pdf_path + "HSBC B24102883_fillable_Financial Needs Analysis for Individual Nov2024.pdf");
        if (!pdfFile.exists()) {
			throw new UnprocessableEntityException("PDF template not found.");
        }
        try (InputStream is = pdfFile.getInputStream()) {
			return is.readAllBytes();
        } catch (Exception e) {
			throw new UnprocessableEntityException("Fail to load PDF: " + e);
		}

        // return fileService.getfileBlob((long) 2);
	}

	public List<Map<String,Object>> list(Map<String, Object> args){
		StringBuilder sql = new StringBuilder("SELECT"
			+ " id, "
			+ " templateId, "
			+ " fileId, "
			+ " created, "
			+ " modified "
			+ " FROM filled_form "
			+ " WHERE deleted = FALSE "
		);

		if (args != null) {
			if (args.containsKey("clientId")) 
				sql.append(" AND clientId = :clientId ");
			if (args.containsKey("templateId")) 
				sql.append(" AND templateId = :templateId ");
			if (args.containsKey("remarks")) 
				sql.append(" AND remarks LIKE :remarks ");
			if (args.containsKey("createDateFrom")) 
				sql.append(" AND created >= :createDateFrom ");
			if (args.containsKey("createDateTo")) 
				sql.append(" AND created <= :createDateTo ");
		}
		// sql.append(" ORDER BY id desc ");

		return jdbcDao.queryForList(sql.toString(), args);
	}

	public Map<String, Object> loadPDF(Long id) {
		// Pdf pdfInstance = find(id).orElseThrow(NotFoundException::new);
		// byte[] blobBytes = fileService.getfileBlob(pdfInstance.getFileId());

		StringBuilder sql = new StringBuilder("SELECT"
			+ " ff.*, "
			+ " fb.bytes AS blobValue"
			+ " FROM filled_form ff "
			+ " LEFT JOIN file_blob fb ON ff.fileId = fb.fileId "
			+ " WHERE ff.deleted = FALSE"
			+ " AND ff.id = :id "
        );
        // Map<String, Object> result = jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new);
		// result.put("blobValue", new ByteArrayResource(blobBytes));
		// return result;
        return jdbcDao.queryForMap(sql.toString(), Map.of("id", id)).orElseThrow(NotFoundException::new);
    }

	public Map<String, Object> getPdfBlob(Long id) {
		String sql = "SELECT " +
			" fb.*, " +
			" fb.bytes AS blobValue " +
			" FROM file_blob fb " +
			" WHERE fb.deleted = FALSE " +
			" AND fb.id = :id ";
			
		return jdbcDao.queryForMap(sql, Map.of("id", id)).orElseThrow(NotFoundException::new);
    }
	
	public Integer getIdByName(String name) {
        StringBuilder sql = new StringBuilder("SELECT"
			+ " e.id "
			+ " FROM event e  "
			+ " WHERE e.deleted = FALSE"
			+ " AND e.name = :name "
        );
        
        return jdbcDao.queryForInt(sql.toString(), Map.of("name", name));
    }
	
}