package com.smartoffice.auth;

import com.smartoffice.common.ApiException;
import com.smartoffice.user.Role;
import com.smartoffice.user.User;
import com.smartoffice.user.UserDto;
import com.smartoffice.user.UserRepository;
import com.smartoffice.user.UserStatus;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.Instant;

@Service
public class AuthService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final AuthenticationManager authenticationManager;
    private final JwtService jwtService;
    private final int maxAttempts;
    private final int lockMinutes;

    public AuthService(UserRepository userRepository,
                       PasswordEncoder passwordEncoder,
                       AuthenticationManager authenticationManager,
                       JwtService jwtService,
                       @Value("${app.security.lockout.max-attempts}") int maxAttempts,
                       @Value("${app.security.lockout.lock-minutes}") int lockMinutes) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.authenticationManager = authenticationManager;
        this.jwtService = jwtService;
        this.maxAttempts = maxAttempts;
        this.lockMinutes = lockMinutes;
    }

    @Transactional
    public AuthResponse login(LoginRequest request) {
        User user = userRepository.findByUsername(request.getUsername())
                .orElseThrow(() -> new ApiException(401, "Invalid credentials"));

        if (user.getStatus() == UserStatus.DISABLED) {
            throw new ApiException(403, "Account disabled");
        }
        if (user.getLockedUntil() != null && user.getLockedUntil().isAfter(Instant.now())) {
            throw new ApiException(423, "Account locked until " + user.getLockedUntil());
        }
        if (user.getStatus() == UserStatus.LOCKED && user.getLockedUntil() == null) {
            throw new ApiException(423, "Account locked");
        }
        if (user.getLockedUntil() != null && user.getLockedUntil().isBefore(Instant.now())) {
            user.setLockedUntil(null);
            user.setStatus(UserStatus.ACTIVE);
            user.setFailedLoginAttempts(0);
            userRepository.save(user);
        }

        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
            UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
            user.setFailedLoginAttempts(0);
            user.setLockedUntil(null);
            user.setLastLoginAt(Instant.now());
            user.setStatus(UserStatus.ACTIVE);
            userRepository.save(user);
            String token = jwtService.generateToken(principal);
            return new AuthResponse(token, UserDto.from(user));
        } catch (Exception ex) {
            int attempts = user.getFailedLoginAttempts() + 1;
            user.setFailedLoginAttempts(attempts);
            if (attempts >= maxAttempts) {
                user.setLockedUntil(Instant.now().plusSeconds(lockMinutes * 60L));
                user.setStatus(UserStatus.LOCKED);
            }
            userRepository.save(user);
            throw new ApiException(401, "Invalid credentials");
        }
    }

    @Transactional
    public UserDto register(RegisterRequest request) {
        if (userRepository.existsByUsername(request.getUsername())) {
            throw new ApiException(409, "Username already exists");
        }
        User user = new User();
        user.setUsername(request.getUsername());
        user.setPasswordHash(passwordEncoder.encode(request.getPassword()));
        user.setFullName(request.getFullName());
        user.setEmail(request.getEmail());
        user.setPhone(request.getPhone());
        user.setRole(Role.EMPLOYEE);
        user.setStatus(UserStatus.ACTIVE);
        userRepository.save(user);
        return UserDto.from(user);
    }
}
