package com.ffii.fhsmsc.modules.common.service;

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

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.ffii.core.support.AbstractService;
import com.ffii.core.support.JdbcDao;
import com.ffii.core.utils.MapUtils;

import jakarta.annotation.Nullable;

@Service
public class AuditLogService extends AbstractService {

	public AuditLogService(JdbcDao jdbcDao) {
		super(jdbcDao);
	  }

	private static final String SQL_INSERT_AUDIT_LOG = "INSERT INTO audit_log (`tableName`, `recordId`, `recordName`, `modifiedBy`, `modified`, `oldData`, `newData`) "
			+ "VALUES (:tableName, :recordId, :recordName, :modifiedBy, :modified, :oldData, :newData)";

	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public int save(String tableName, Long recordId, String recordName, Long modifiedBy, Date modified, @Nullable String oldData, String newData) {
		return jdbcDao.executeUpdate(SQL_INSERT_AUDIT_LOG,MapUtils.toHashMap("tableName", tableName, "recordId", recordId, 
		"recordName",recordName, "modifiedBy", modifiedBy, "modified", modified,
		"oldData", oldData, "newData", newData));
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public List<Map<String, Object>> search(String tableName, Integer recordId) {

		String sql = "SELECT * FROM audit_log WHERE tableName = :tableName AND recordId = :recordId ORDER BY modified";

		return jdbcDao.queryForList(sql, Map.of("tableName", tableName, "recordId", recordId));
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public List<Map<String, Object>> search(String tableName, String recordId) {

		String sql = "SELECT * FROM audit_log WHERE tableName = :tableName AND recordId = :recordId ORDER BY modified";

		return jdbcDao.queryForList(sql, Map.of("tableName", tableName, "recordId", recordId));
	}

	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public List<Map<String, Object>> arsSearch(Map<String,Object> args) {
		StringBuilder sql = new StringBuilder("SELECT"
			+ " *, " 
			+ " u.username "
			+ " FROM audit_log al" 
			+ " LEFT JOIN user u on u.id = al.modifiedBy "
			+ " WHERE al.tableName is not NULL " 
		);
		
		if (args != null) {
			if (args.containsKey("tableName")) sql.append(" AND al.tableName = :tableName ");
			if (args.containsKey("recordName")) sql.append(" AND al.recordName LIKE :recordName");
			if (args.containsKey("fromDate"))sql.append(" AND DATE(al.modified) >= :fromDate");
			if (args.containsKey("toDate"))sql.append(" AND DATE(al.modified) < :toDate");
			if (args.containsKey("modifiedBy")) sql.append(" AND al.modifiedBy = :modifiedBy");
		}
		sql.append(" ORDER BY al.modified desc ");
		return jdbcDao.queryForList(sql.toString(), args);
	}


	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public List<Map<String, Object>> getTables() {
		String sql = "SELECT DISTINCT tableName FROM audit_log";

		return jdbcDao.queryForList(sql, "");
	}

	public Map<String, Object> compareMaps(Map<String, Object> map1, Map<String, Object> map2) {
        Map<String, Object> diffMap = new HashMap<>();

        // Iterate over the entries of map2
        for (Map.Entry<String, Object> entry : map2.entrySet()) {
            String key = entry.getKey();
            Object value2 = entry.getValue();
            Object value1 = map1.get(key);

            // Compare the values
			if (value1 != null) {
                if (value1.getClass().isArray() && value2.getClass().isArray()) {
                    if (!Arrays.deepEquals((Object[]) value1, (Object[]) value2)) {
                        diffMap.put(key, value2);
                    }
                } else if (!value1.equals(value2)) {
                    diffMap.put(key, value2);
                }
            }
			else if(value2 != null){
				diffMap.put(key, value2);
			}
        }

        return diffMap;
    }
}
