/*******************************************************************************
 * Copyright 2019 2Fi Business Solutions Ltd.
 *
 * This code is part of the Core project.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.tbms.user.service;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.ffii.core.dao.JdbcDao;
import com.ffii.core.setting.Setting;
import com.ffii.core.utils.MapUtils;
import com.ffii.core.utils.NumberUtils;
import com.ffii.core.utils.Params;
import com.ffii.core.utils.SecurityUtils;

/**
 * System Settings Service
 *
 * @author Patrick
 */
@Service
public class UserSettingsService {

	private static final String SQL_INSERT_OR_UPDATE_SETTING = "INSERT INTO user_settings (`userId`,`name`, `value`) VALUES (:userId, :name, :value) ON DUPLICATE KEY UPDATE `userId` = VALUES(`userId`),`name` = VALUES(`name`), `value` = VALUES(`value`)";

	/**
	 * Settings cache
	
	private static Map<String, Setting> SETTINGS = null;
	 */

	@Autowired
	private JdbcDao jdbcDao;

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

		int userId = SecurityUtils.getUser().getId();
		args.put("userId", userId);


		StringBuilder sql = new StringBuilder("SELECT * FROM user_settings WHERE 1=1");

		if (args.containsKey("category")) sql.append(" AND category = :category");
		if (args.containsKey("userId")) sql.append(" AND userId = :userId");
		if (args.containsKey("name")) sql.append(" AND name = :name");

		sql.append(" ORDER BY category, name");

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


	/**
	 * @param name
	 *            the name of the setting
	 * @param defaultValue
	 *            the default value to return if setting is not found
	 *
	 * @return the String value of the setting, or the default value if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public String getString(String name, String defaultValue) {
		List<Map<String, Object>> userSettings = searchSettings(MapUtils.toHashMap(Params.NAME, name));
		if(userSettings == null) return defaultValue;
		return userSettings.get(0).get(name) != null ? userSettings.get(0).get(name).toString() : defaultValue;
	}

	/**
	 * @param name
	 *            the name of the setting
	 *
	 * @return the String value of the setting, or {@code null} if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public String getString(String name) {
		return getString(name, null);
	}

	/**
	 * @param name
	 *            the name of the setting
	 * @param defaultValue
	 *            the default value to return if setting is not found
	 *
	 * @return the int value of the setting, or the default value if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public int getInt(String name, int defaultValue) {
		return NumberUtils.toInt(getString(name), defaultValue);
	}

	/**
	 * @param name
	 *            the name of the setting
	 *
	 * @return the int value of the setting, or zero (0) if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public int getInt(String name) {
		return getInt(name, 0);
	}

	/**
	 * @param name
	 *            the name of the setting
	 * @param defaultValue
	 *            the default value to return if setting is not found
	 *
	 * @return the double value of the setting, or the default value if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public double getDouble(String name, double defaultValue) {
		return NumberUtils.toDouble(getString(name), defaultValue);
	}

	/**
	 * @param name
	 *            the name of the setting
	 *
	 * @return the double value of the setting, or zero (0.0) if not found
	 */
	@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	public double getDouble(String name) {
		return getDouble(name, 0.0d);
	}

	/**
	 * @param name
	 *            the name of the setting
	 * @param value
	 *            the value of the setting
	 */
	@Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class, readOnly = false)
	public int saveSetting(String name, String value) {
		int userId = SecurityUtils.getUser().getId();
		Map<String, Object> args = new HashMap<String, Object>();
		args.put("userId", userId);
		args.put(Params.NAME, name);
		args.put(Params.VALUE, value);
		return jdbcDao.executeUpdate(SQL_INSERT_OR_UPDATE_SETTING, args);
	}

}
