package com.mzh.library.service.impl;

import com.mzh.library.dao.SystemLogDao;
import com.mzh.library.dao.UserAccountDao;
import com.mzh.library.entity.AuthenticatedUser;
import com.mzh.library.entity.Permission;
import com.mzh.library.entity.Role;
import com.mzh.library.entity.SystemLog;
import com.mzh.library.entity.User;
import com.mzh.library.entity.UserSearchCriteria;
import com.mzh.library.exception.DaoException;
import com.mzh.library.service.PermissionPolicy;
import com.mzh.library.service.ServiceResult;
import com.mzh.library.service.UserAccountService;
import com.mzh.library.util.JdbcUtil;
import com.mzh.library.util.PasswordHasher;

import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

public class UserAccountServiceImpl implements UserAccountService {
    public interface TransactionExecutor {
        <T> T execute(JdbcUtil.TransactionCallback<T> callback);
    }

    private static final Logger LOGGER = Logger.getLogger(UserAccountServiceImpl.class.getName());
    private static final String UNAVAILABLE_MESSAGE =
        "用户管理服务暂时不可用，请稍后重试。";
    private static final String VALIDATION_MESSAGE = "请修正高亮的账户字段。";
    private static final String SEARCH_VALIDATION_MESSAGE = "请修正账户检索筛选条件。";
    private static final String DENIED_MESSAGE = "您无权管理用户。";
    private static final String SELF_DEACTIVATE_MESSAGE = "不能停用您自己的管理员账户。";
    private static final String SELF_ROLE_MESSAGE = "不能修改您自己的管理员角色。";

    private final UserAccountDao userAccountDao;
    private final SystemLogDao systemLogDao;
    private final PermissionPolicy permissionPolicy;
    private final TransactionExecutor transactionExecutor;

    public UserAccountServiceImpl(UserAccountDao userAccountDao, SystemLogDao systemLogDao) {
        this(userAccountDao, systemLogDao, new PermissionPolicy(), new JdbcTransactionExecutor());
    }

    public UserAccountServiceImpl(UserAccountDao userAccountDao, SystemLogDao systemLogDao,
                                  PermissionPolicy permissionPolicy, TransactionExecutor transactionExecutor) {
        this.userAccountDao = userAccountDao;
        this.systemLogDao = systemLogDao;
        this.permissionPolicy = permissionPolicy;
        this.transactionExecutor = transactionExecutor;
    }

    @Override
    public ServiceResult<List<User>> searchUsers(AuthenticatedUser actor, UserSearchCriteria criteria) {
        if (!canManageUsers(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        UserSearchCriteria normalized = criteria == null ? new UserSearchCriteria() : criteria;
        Map<String, String> errors = validateSearch(normalized);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(SEARCH_VALIDATION_MESSAGE, errors);
        }

        try {
            return ServiceResult.success(userAccountDao.search(normalized));
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to search users actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Optional<User>> findUser(AuthenticatedUser actor, long id) {
        if (!canManageUsers(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的用户账户。");
        }

        try {
            return ServiceResult.success(userAccountDao.findById(id));
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to load user id=" + id + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Long> createUser(AuthenticatedUser actor, User user, String password, String requestIp) {
        if (!canManageUsers(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(user);
        Map<String, String> errors = validateUser(user, false, password, true);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
        }

        try {
            if (userAccountDao.findByUsername(user.getUsername()).isPresent()) {
                errors.put("username", "用户名已被使用。");
                return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
            }

            user.setPasswordHash(PasswordHasher.hash(password));
            return transactionExecutor.execute(connection -> {
                long id = userAccountDao.create(connection, user);
                systemLogDao.create(connection, auditLog(actor, "user.create", id,
                    "创建账户：用户名=" + user.getUsername() + "，角色=" + user.getRole().getDisplayName(),
                    requestIp));
                LOGGER.info("Created user id=" + id + " actorId=" + actor.getId());
                return ServiceResult.success(id, "用户账户已创建。");
            });
        } catch (DaoException | IllegalStateException ex) {
            LOGGER.log(Level.SEVERE, "Unable to create user actorId=" + actor.getId()
                + " username=" + safeUsername(user), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> updateUser(AuthenticatedUser actor, User user, String password, String requestIp) {
        if (!canManageUsers(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }

        normalize(user);
        Map<String, String> errors = validateUser(user, true, password, false);
        if (!errors.isEmpty()) {
            return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
        }

        try {
            Optional<User> existingResult = userAccountDao.findById(user.getId());
            if (!existingResult.isPresent()) {
                return ServiceResult.failure("未找到用户账户。");
            }

            protectCurrentAdministrator(actor, user, errors);
            if (!errors.isEmpty()) {
                return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
            }

            User existing = existingResult.get();
            user.setUsername(existing.getUsername());
            boolean updatePassword = password != null && !password.trim().isEmpty();
            if (updatePassword) {
                user.setPasswordHash(PasswordHasher.hash(password));
            }

            final boolean passwordChanged = updatePassword;
            return transactionExecutor.execute(connection -> {
                if (!userAccountDao.update(connection, user, passwordChanged)) {
                    return ServiceResult.failure("未找到用户账户。");
                }
                systemLogDao.create(connection, auditLog(actor, "user.update", user.getId(),
                    "更新账户：用户名=" + user.getUsername() + "，角色=" + user.getRole().getDisplayName()
                        + "，状态=" + (user.isActive() ? "启用" : "停用")
                        + (passwordChanged ? "，已重置密码" : ""),
                    requestIp));
                LOGGER.info("Updated user id=" + user.getId() + " actorId=" + actor.getId());
                return ServiceResult.success(null, "用户账户已更新。");
            });
        } catch (DaoException | IllegalStateException ex) {
            LOGGER.log(Level.SEVERE, "Unable to update user id=" + user.getId() + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public ServiceResult<Void> deactivateUser(AuthenticatedUser actor, long id, String requestIp) {
        if (!canManageUsers(actor)) {
            return ServiceResult.failure(DENIED_MESSAGE);
        }
        if (id <= 0) {
            return ServiceResult.failure("请选择有效的用户账户。");
        }
        if (actor.getId() == id) {
            Map<String, String> errors = new LinkedHashMap<>();
            errors.put("active", SELF_DEACTIVATE_MESSAGE);
            return ServiceResult.validationFailure(VALIDATION_MESSAGE, errors);
        }

        try {
            Optional<User> existingResult = userAccountDao.findById(id);
            if (!existingResult.isPresent()) {
                return ServiceResult.failure("未找到用户账户。");
            }

            User user = existingResult.get();
            user.setActive(false);
            return transactionExecutor.execute(connection -> {
                if (!userAccountDao.update(connection, user, false)) {
                    return ServiceResult.failure("未找到用户账户。");
                }
                systemLogDao.create(connection, auditLog(actor, "user.deactivate", id,
                    "停用账户：用户名=" + user.getUsername(),
                    requestIp));
                LOGGER.info("Deactivated user id=" + id + " actorId=" + actor.getId());
                return ServiceResult.success(null, "用户账户已停用。");
            });
        } catch (DaoException ex) {
            LOGGER.log(Level.SEVERE, "Unable to deactivate user id=" + id + " actorId=" + actor.getId(), ex);
            return ServiceResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    private Map<String, String> validateSearch(UserSearchCriteria criteria) {
        Map<String, String> errors = new LinkedHashMap<>();
        if (!criteria.getRoleCode().isEmpty()) {
            try {
                criteria.setRoleCode(Role.fromCode(criteria.getRoleCode()).getCode());
            } catch (IllegalArgumentException ex) {
                errors.put("role", "请选择有效的角色。");
            }
        }

        String activeStatus = criteria.getActiveStatus();
        if (!activeStatus.isEmpty()
            && !UserSearchCriteria.ACTIVE_STATUS.equals(activeStatus)
            && !UserSearchCriteria.INACTIVE_STATUS.equals(activeStatus)) {
            errors.put("active", "请选择有效的启用状态。");
        }
        return errors;
    }

    private Map<String, String> validateUser(User user, boolean requireId, String password, boolean requirePassword) {
        Map<String, String> errors = new LinkedHashMap<>();
        if (user == null) {
            errors.put("user", "请填写用户账户详情。");
            return errors;
        }

        if (requireId && user.getId() <= 0) {
            errors.put("id", "请选择有效的用户账户。");
        }
        if (!requireId) {
            requireLength(errors, "username", user.getUsername(), "用户名", 64);
        }
        requireLength(errors, "displayName", user.getDisplayName(), "显示名称", 100);
        if (user.getRole() == null) {
            errors.put("role", "请选择角色。");
        }
        validatePassword(errors, password, requirePassword);
        return errors;
    }

    private void validatePassword(Map<String, String> errors, String password, boolean required) {
        String trimmed = password == null ? "" : password.trim();
        if (trimmed.isEmpty()) {
            if (required) {
                errors.put("password", "请填写密码。");
            }
            return;
        }
        if (password.length() > 128) {
            errors.put("password", "密码不能超过 128 个字符。");
        }
    }

    private void protectCurrentAdministrator(AuthenticatedUser actor, User user, Map<String, String> errors) {
        if (actor.getId() != user.getId()) {
            return;
        }
        if (!user.isActive()) {
            errors.put("active", SELF_DEACTIVATE_MESSAGE);
        }
        if (user.getRole() != Role.ADMINISTRATOR) {
            errors.put("role", SELF_ROLE_MESSAGE);
        }
    }

    private void requireLength(Map<String, String> errors, String field, String value, String label, int maxLength) {
        if (value == null || value.isEmpty()) {
            errors.put(field, "请填写" + label + "。");
            return;
        }
        if (value.length() > maxLength) {
            errors.put(field, label + "不能超过 " + maxLength + " 个字符。");
        }
    }

    private SystemLog auditLog(AuthenticatedUser actor, String operationType, long userId, String message,
                               String requestIp) {
        SystemLog log = new SystemLog();
        log.setOperatorId(actor.getId());
        log.setOperatorRole(actor.getRole().getCode());
        log.setOperationType(operationType);
        log.setTargetTable("users");
        log.setTargetId(String.valueOf(userId));
        log.setResultStatus("success");
        log.setMessage(message);
        log.setRequestIp(trim(requestIp));
        return log;
    }

    private boolean canManageUsers(AuthenticatedUser actor) {
        return actor != null && permissionPolicy.allows(actor.getRole(), Permission.MANAGE_USERS);
    }

    private void normalize(User user) {
        if (user == null) {
            return;
        }
        user.setUsername(normalizeUsername(user.getUsername()));
        user.setDisplayName(trim(user.getDisplayName()));
    }

    private String normalizeUsername(String username) {
        return trim(username);
    }

    private String safeUsername(User user) {
        return user == null ? "" : user.getUsername();
    }

    private String trim(String value) {
        return value == null ? "" : value.trim();
    }

    private static final class JdbcTransactionExecutor implements TransactionExecutor {
        @Override
        public <T> T execute(JdbcUtil.TransactionCallback<T> callback) {
            return JdbcUtil.executeInTransaction(callback);
        }
    }

    public static final class DirectTransactionExecutor implements TransactionExecutor {
        @Override
        public <T> T execute(JdbcUtil.TransactionCallback<T> callback) {
            try {
                return callback.execute(null);
            } catch (SQLException ex) {
                throw new DaoException("Unable to execute direct transaction", ex);
            }
        }
    }
}
