package com.mzh.library.service.impl;

import com.mzh.library.dao.UserDao;
import com.mzh.library.entity.AuthenticatedUser;
import com.mzh.library.entity.Permission;
import com.mzh.library.entity.User;
import com.mzh.library.exception.DaoException;
import com.mzh.library.service.AuthService;
import com.mzh.library.service.AuthenticationResult;
import com.mzh.library.service.PermissionPolicy;
import com.mzh.library.util.PasswordHasher;

import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AuthServiceImpl implements AuthService {
    private static final Logger LOGGER = Logger.getLogger(AuthServiceImpl.class.getName());
    private static final String REQUIRED_MESSAGE = "请输入用户名和密码。";
    private static final String INVALID_MESSAGE = "用户名或密码不正确。";
    private static final String UNAVAILABLE_MESSAGE = "登录服务暂时不可用，请稍后重试。";

    private final UserDao userDao;
    private final PermissionPolicy permissionPolicy;

    public AuthServiceImpl(UserDao userDao) {
        this(userDao, new PermissionPolicy());
    }

    public AuthServiceImpl(UserDao userDao, PermissionPolicy permissionPolicy) {
        this.userDao = userDao;
        this.permissionPolicy = permissionPolicy;
    }

    @Override
    public AuthenticationResult authenticate(String username, String password) {
        String normalizedUsername = normalizeUsername(username);
        if (!normalizedUsername.equals(nullToEmpty(username))) {
            LOGGER.info("Login username normalized"
                + " usernameSubmitted=" + (username != null)
                + " usernameLength=" + length(username)
                + " normalizedUsernameLength=" + normalizedUsername.length()
                + " normalizedUsername=" + safeLogValue(normalizedUsername));
        }

        boolean usernameMissing = normalizedUsername.isEmpty();
        boolean passwordMissing = password == null || password.trim().isEmpty();
        if (usernameMissing || passwordMissing) {
            LOGGER.info("Login rejected reason=missing-required"
                + " usernameSubmitted=" + (username != null)
                + " usernameMissing=" + usernameMissing
                + " passwordSubmitted=" + (password != null)
                + " passwordMissing=" + passwordMissing);
            return AuthenticationResult.failure(REQUIRED_MESSAGE);
        }

        try {
            LOGGER.info("Login lookup start username=" + safeLogValue(normalizedUsername));
            Optional<User> user = userDao.findActiveByUsername(normalizedUsername);
            if (!user.isPresent()) {
                LOGGER.info("Login failed reason=active-user-not-found username=" + safeLogValue(normalizedUsername));
                return AuthenticationResult.failure(INVALID_MESSAGE);
            }

            User candidate = user.get();
            if (!PasswordHasher.verify(password, candidate.getPasswordHash())) {
                LOGGER.info("Login failed reason=password-mismatch"
                    + " userId=" + candidate.getId()
                    + " role=" + candidate.getRole().getCode()
                    + " username=" + safeLogValue(normalizedUsername));
                return AuthenticationResult.failure(INVALID_MESSAGE);
            }

            User authenticated = candidate;
            Set<Permission> permissions = permissionPolicy.permissionsFor(authenticated.getRole());
            AuthenticatedUser sessionUser = AuthenticatedUser.from(authenticated, permissions);
            LOGGER.info("Login success userId=" + authenticated.getId() + " role=" + authenticated.getRole().getCode());
            return AuthenticationResult.success(sessionUser);
        } catch (DaoException | IllegalStateException ex) {
            LOGGER.log(Level.SEVERE, "Login service error for username=" + safeLogValue(normalizedUsername), ex);
            return AuthenticationResult.failure(UNAVAILABLE_MESSAGE);
        }
    }

    @Override
    public boolean hasPermission(AuthenticatedUser user, Permission permission) {
        return user != null && permissionPolicy.allows(user.getRole(), permission);
    }

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

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

    private int length(String value) {
        return value == null ? 0 : value.length();
    }

    private String safeLogValue(String value) {
        if (value == null) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        int limit = Math.min(value.length(), 120);
        for (int i = 0; i < limit; i++) {
            char current = value.charAt(i);
            builder.append(Character.isISOControl(current) ? '?' : current);
        }
        if (value.length() > limit) {
            builder.append("...");
        }
        return builder.toString();
    }
}
