package com.ffii.lioner.modules.master.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.common.SecurityUtils;
import com.ffii.lioner.modules.common.service.AuditLogService;
import com.ffii.lioner.modules.master.entity.Division;
import com.ffii.lioner.modules.master.entity.DivisionRepository;
import com.ffii.lioner.modules.master.req.UpdateDivisionReq;
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 DivisionService extends AbstractBaseEntityService<Division, Long, DivisionRepository> {

	private SubDivisionService subDivisionService;
	private AuditLogService auditLogService;

	public DivisionService(JdbcDao jdbcDao, DivisionRepository repository, SubDivisionService subDivisionService, AuditLogService auditLogService) {
		super(jdbcDao, repository);
		this.subDivisionService = subDivisionService;
		this.auditLogService = auditLogService;
	}

	public Map<String,Object> getAuditLogObject(Map<String,Object> req){
		StringBuilder sql = new StringBuilder("SELECT"
			+ " d.id,    "
			+ " d.created,    "
			+ " d.createdBy AS createdById,    "
			+ " u1.name AS createdBy,    "
			+ " d.version,    "
			+ " d.modified,    "
			+ " d.modifiedBy AS modifiedById,    "
			+ " u2.name AS modifiedBy, "
			+ " d.deleted, "
			+ " d.name, "
			+ " d.branch "
			+ " FROM division d   "
			+ " LEFT JOIN `user` u1 ON u1.id = d.createdBy     "
			+ " LEFT JOIN `user` u2 ON u2.id = d.modifiedBy      "
			+ " WHERE d.id = :id    "
		);

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


	public Map<String, Object> saveOrUpdate(UpdateDivisionReq req) {
		Division instance;
		if(req.getId()>0){
			instance = find(req.getId()).get();
		}
		else{
			instance = new Division();
		}

		//=====GET OLD AUDIT LOG=====//
		String tableName = instance.getClass().getAnnotation(Table.class).name();
		StringBuilder sql = new StringBuilder("SELECT * FROM " + tableName + " WHERE id = :id");
		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);
			logData.put("subDivision", subDivisionService.listSubdivisionName(input));
			logData.put("subDivisionIds", subDivisionService.listSubdivisionId(input));
			oldValueObject = logData;
		}
		//=====GET OLD AUDIT LOG=====//

		BeanUtils.copyProperties(req,instance);
		instance = save(instance);
		List<Long> subDivisionIdList = subDivisionService.saveOrUpdateList(req.getSubDivisionList(), instance.getId());
		List<Long> deletedList = subDivisionService.deleteByIdList(req.getSubDivisionDeleteList());

		Long id = 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);
			logData.put("subDivision", subDivisionService.listSubdivisionName(input));
			logData.put("subDivisionIds", subDivisionService.listSubdivisionId(input));
			newValueObject = logData;
		}

		if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 ||
			auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0
		){
			auditLogService.save(
				tableName,
				id,
				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 Map.of(
			"id", instance.getId(),
			"subDivisionId", subDivisionIdList,
			"deletedSubId", deletedList
		);
	}

	public Map<String, Object> softDeleteWithSubDivision(Long id){
		Division instance = this.find(id).orElseThrow(NotFoundException::new);
		//=====GET OLD AUDIT LOG=====//
		String tableName = instance.getClass().getAnnotation(Table.class).name();
		StringBuilder sql = new StringBuilder("SELECT * FROM " + tableName + " WHERE id = :id");
		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);
			logData.put("subDivision", subDivisionService.listSubdivisionName(input));
			logData.put("subDivisionIds", subDivisionService.listSubdivisionId(input));
			oldValueObject = logData;
		}
		//=====GET OLD AUDIT LOG=====//
		this.markDelete(instance);
		subDivisionService.deleteByDivision(id);
		//=====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);
			logData.put("subDivision", subDivisionService.listSubdivisionName(input));
			logData.put("subDivisionIds", subDivisionService.listSubdivisionId(input));
			newValueObject = logData;
		}


		if(auditLogService.compareMaps(newValueObject,oldValueObject).size() != 0 ||
			auditLogService.compareMaps(oldValueObject,newValueObject).size() != 0
		){
			auditLogService.save(
				tableName,
				id,
				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 Map.of(
			"id", id
		);
	}

	public List<Map<String,Object>> listDivision(Map<String, Object> args){
		StringBuilder sql = new StringBuilder("SELECT"
			+ " d.id, "
			+ " d.name, "
			+ " d.branch, "
			+ " '' as subDivision "
			+ " FROM division d  "
			+ " WHERE d.deleted = FALSE "
		);
		if (args != null) {
			if (args.containsKey(Params.ID)) sql.append(" AND d.id = :id");
			if (args.containsKey(Params.NAME)) sql.append(" AND d.name LIKE :name");
			if (args.containsKey("branch")) sql.append(" AND d.branch LIKE :branch");
		}
		sql.append(" ORDER BY d.name asc");

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

	public String isNameTaken(Map<String,Object> arg) {
        StringBuilder sql = new StringBuilder("SELECT"
            + " d.name "
            + " FROM division d  "
            + " WHERE d.deleted = FALSE  "
            + " AND d.name = :division "
			+ " AND d.id != :divisionId"
        );
        
        return jdbcDao.queryForString(sql.toString(), arg);
    }

}
