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

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.ffii.core.utils.BeanUtils;
import org.springframework.stereotype.Service;

import com.ffii.lioner.modules.lioner.entity.Event;
import com.ffii.lioner.modules.lioner.entity.SearchCriteriaTemplate;
import com.ffii.lioner.modules.lioner.entity.SearchCriteriaTemplateRepository;
import com.ffii.lioner.modules.lioner.req.UpdateSearchCriteriaTemplateReq;
import com.ffii.lioner.modules.common.SecurityUtils;
import com.ffii.lioner.modules.common.service.AuditLogService;
import com.ffii.core.exception.NotFoundException;
import com.ffii.core.support.AbstractBaseEntityService;
import com.ffii.core.support.JdbcDao;
import com.ffii.core.utils.JsonUtils;
import com.ffii.core.utils.Params;

import jakarta.persistence.Table;

@Service
public class SearchCriteriaTemplateService extends AbstractBaseEntityService<SearchCriteriaTemplate, Long, SearchCriteriaTemplateRepository> {

	private AuditLogService auditLogService;
	public SearchCriteriaTemplateService(JdbcDao jdbcDao, SearchCriteriaTemplateRepository repository, AuditLogService auditLogService) {
		super(jdbcDao, repository);
		this.auditLogService = auditLogService;
	}

	public Map<String,Object> getAuditLogObject(Map<String,Object> req){
		StringBuilder sql = new StringBuilder("SELECT"
			+ " sct.id,   "
			+ " sct.created,   "
			+ " sct.createdBy AS createdById,   "
			+ " u1.name AS createdBy,   "
			+ " sct.version,   "
			+ " sct.modified,   "
			+ " sct.modifiedBy AS modifiedById,   "
			+ " sct.deleted, "
			+ " u2.name AS modifiedBy,   "
			+ " sct.userId, "
			+ " u.username AS userName, "
			+ " sct.name, "
			+ " sct.module, "
			+ " sct.criteria  "
			+ " FROM search_criteria_template sct 	  "
			+ " LEFT JOIN `user` u1 ON u1.id = sct.createdBy    "
			+ " LEFT JOIN `user` u2 ON u2.id = sct.modifiedBy    "
			+ " JOIN `user` u ON u.id = sct.userId   "
			+ " WHERE sct.id = :id   "
		);

		return jdbcDao.queryForMap(sql.toString(), req).get();
	}

	public Long saveOrUpdate(UpdateSearchCriteriaTemplateReq req) {
        SearchCriteriaTemplate instance;
		if(req.getId() > 0){
			instance = find(req.getId()).get();
		}
		else{
			instance = new SearchCriteriaTemplate();
		}
		//=====GET OLD AUDIT LOG=====//
		String tableName = instance.getClass().getAnnotation(Table.class).name();
		Map<String,Object> oldValueObject = new HashMap<String,Object>();
		Map<String,Object> newValueObject = new HashMap<String,Object>();
		
		if (instance != null && instance.getId() != null && instance.getId() > 0) {
			Map<String,Object> input = Map.of("id", instance.getId());
			Map<String,Object> logData= getAuditLogObject(input);
			oldValueObject = logData;
		}
		//=====GET OLD AUDIT LOG=====//
        BeanUtils.copyProperties(req,instance);
		instance = save(instance);
		//=====GET NEW AUDIT LOG=====//
		if (instance != null && instance.getId() != null && instance.getId() > 0) {
			Map<String,Object> input = Map.of("id", instance.getId());
			Map<String,Object> logData= getAuditLogObject(input);
			newValueObject = logData;
		}
		
		
		if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 ||
			auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0
		){
			auditLogService.save(
				tableName,
				instance.getId(),
				instance.getName(),
				SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null,
				new Date(),
				JsonUtils.toJsonString(auditLogService.compareMaps(newValueObject,oldValueObject)),
				JsonUtils.toJsonString(auditLogService.compareMaps(oldValueObject,newValueObject)));
		}
		//=====GET NEW AUDIT LOG=====//
		return instance.getId();
	}

	public void markDeleteWithAuditLog(SearchCriteriaTemplate instance){
		//=====GET OLD AUDIT LOG=====//
		String tableName = instance.getClass().getAnnotation(Table.class).name();
		Map<String,Object> oldValueObject = new HashMap<String,Object>();
		Map<String,Object> newValueObject = new HashMap<String,Object>();
		
		if (instance != null && instance.getId() != null && instance.getId() > 0) {
			Map<String,Object> input = Map.of("id", instance.getId());
			Map<String,Object> logData= getAuditLogObject(input);
			oldValueObject = logData;
		}
		//=====GET OLD AUDIT LOG=====//
		this.markDelete(instance.getId());
		//=====GET NEW AUDIT LOG=====//
		if (instance != null && instance.getId() != null && instance.getId() > 0) {
			Map<String,Object> input = Map.of("id", instance.getId());
			Map<String,Object> logData= getAuditLogObject(input);
			newValueObject = logData;
		}

		
		if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 ||
			auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0
		){
			auditLogService.save(
				tableName,
				instance.getId(),
				instance.getName(),
				SecurityUtils.getUser() != null ? SecurityUtils.getUser().get().getId() : null,
				new Date(),
				JsonUtils.toJsonString(auditLogService.compareMaps(newValueObject,oldValueObject)),
				JsonUtils.toJsonString(auditLogService.compareMaps(oldValueObject,newValueObject)));
		}
		//=====GET NEW AUDIT LOG=====//
	}

	public List<Map<String, Object>> search(Map<String, Object> args) {
		StringBuilder sql = new StringBuilder("SELECT"
            + " sct.id, "
            + " sct.module, "
            + " sct.name "
            + " FROM search_criteria_template sct "
            + " WHERE sct.deleted = FALSE "
			+ " AND sct.userId = :userId"
        );

		if (args != null) {
			if (args.containsKey("module")) sql.append(" AND sct.module LIKE :module ");
			if (args.containsKey("templateName")) sql.append(" AND sct.name LIKE :templateName ");
		}

		sql.append(" ORDER BY sct.module asc");

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

	public List<Map<String, Object>> combo(Map<String, Object> args) {
		StringBuilder sql = new StringBuilder("SELECT"
			+ " sct.id, "
			+ " sct.id AS `key`, "
			+ " sct.name as label"
			+ " FROM search_criteria_template sct  "
			+ " WHERE sct.deleted = FALSE  "
			+ " AND sct.module = :module  "
			+ " AND sct.userId = :userId"
        );

		if (args != null) {
			if (args.containsKey("module")) sql.append(" AND sct.module = :module ");
		}

		sql.append(" ORDER BY sct.name asc");

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

    public boolean isTemplateNameTaken(String name, Long userId, Long templateId, String module) {
        StringBuilder sql = new StringBuilder("SELECT"
            + " Count(sct.id) "
            + " FROM search_criteria_template sct  "
            + " WHERE sct.deleted = FALSE  "
            + " AND sct.name = :name "
			+ " AND sct.userId = :userId "
			+ " AND sct.id != :templateId "
			+ " AND sct.module LIKE :module"
        );
        
	    return jdbcDao.queryForBoolean(sql.toString(), Map.of("name", name, "userId", userId,"templateId",templateId, "module", module));
    }
}

