package com.mvrent.carrental.service;

import com.mvrent.carrental.model.Role;
import com.mvrent.carrental.model.User;
import com.mvrent.carrental.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public User register(User user, String rawPassword) {
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new IllegalArgumentException("Username already taken");
        }
        if (userRepository.existsByEmail(user.getEmail())) {
            throw new IllegalArgumentException("Email already registered");
        }
        user.setPassword(passwordEncoder.encode(rawPassword));
        if (user.getRole() == null) user.setRole(Role.CUSTOMER);
        user.setEnabled(true);
        return userRepository.save(user);
    }

    public User findByUsername(String username) {
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new IllegalArgumentException("User not found: " + username));
    }

    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Customer not found: " + id));
    }

    public java.util.List<User> findCustomers() {
        return userRepository.findByRoleOrderByFullName(Role.CUSTOMER);
    }

    /* ---------- Staff / user management (ADMIN + STAFF) ---------- */

    public java.util.List<User> findStaff() {
        return userRepository.findByRoleInOrderByFullName(java.util.List.of(Role.ADMIN, Role.STAFF));
    }

    @org.springframework.transaction.annotation.Transactional
    public User createStaff(com.mvrent.carrental.web.StaffForm f) {
        if (f.username() == null || f.username().isBlank()) throw new IllegalArgumentException("Username is required");
        if (f.email() == null || f.email().isBlank()) throw new IllegalArgumentException("Email is required");
        if (f.password() == null || f.password().length() < 6) throw new IllegalArgumentException("Password must be at least 6 characters");
        if (userRepository.existsByUsername(f.username().trim())) throw new IllegalArgumentException("Username already taken");
        if (userRepository.existsByEmail(f.email().trim())) throw new IllegalArgumentException("Email already registered");

        Role role = (f.role() == Role.ADMIN) ? Role.ADMIN : Role.STAFF;
        User u = new User();
        u.setUsername(f.username().trim());
        u.setFullName(f.fullName() != null && !f.fullName().isBlank() ? f.fullName().trim() : f.username().trim());
        u.setEmail(f.email().trim());
        u.setPhone(f.phone());
        u.setRole(role);
        u.setEnabled(f.enabled() == null || f.enabled());
        u.setPassword(passwordEncoder.encode(f.password()));
        u.setPermissions(resolvePermissions(role, f.permissions()));
        return userRepository.save(u);
    }

    @org.springframework.transaction.annotation.Transactional
    public User updateStaff(com.mvrent.carrental.web.StaffForm f) {
        User u = findById(f.id());
        requireStaff(u);
        if (f.email() != null && !f.email().isBlank()) {
            userRepository.findByEmail(f.email().trim())
                    .filter(other -> !other.getId().equals(u.getId()))
                    .ifPresent(other -> { throw new IllegalArgumentException("Email already registered"); });
            u.setEmail(f.email().trim());
        }
        if (f.fullName() != null && !f.fullName().isBlank()) u.setFullName(f.fullName().trim());
        u.setPhone(f.phone());
        Role role = (f.role() == Role.ADMIN) ? Role.ADMIN : Role.STAFF;
        u.setRole(role);
        if (f.enabled() != null) u.setEnabled(f.enabled());
        u.setPermissions(resolvePermissions(role, f.permissions()));
        return userRepository.save(u);
    }

    @org.springframework.transaction.annotation.Transactional
    public void setEnabled(Long id, boolean enabled) {
        User u = findById(id);
        requireStaff(u);
        if (!enabled) guardLastAdmin(u);
        u.setEnabled(enabled);
        userRepository.save(u);
    }

    @org.springframework.transaction.annotation.Transactional
    public void resetPassword(Long id, String newPassword) {
        if (newPassword == null || newPassword.length() < 6) throw new IllegalArgumentException("Password must be at least 6 characters");
        User u = findById(id);
        requireStaff(u);
        u.setPassword(passwordEncoder.encode(newPassword));
        userRepository.save(u);
    }

    @org.springframework.transaction.annotation.Transactional
    public void deleteStaff(Long id) {
        User u = findById(id);
        requireStaff(u);
        guardLastAdmin(u);
        userRepository.delete(u);
    }

    private void requireStaff(User u) {
        if (u.getRole() != Role.ADMIN && u.getRole() != Role.STAFF) {
            throw new IllegalArgumentException("Not a staff/admin account");
        }
    }

    /** Don't allow removing/disabling the last enabled admin. */
    private void guardLastAdmin(User u) {
        if (u.getRole() == Role.ADMIN && u.isEnabled()
                && userRepository.countByRoleAndEnabledTrue(Role.ADMIN) <= 1) {
            throw new IllegalArgumentException("Cannot remove the last active administrator");
        }
    }

    private java.util.Set<com.mvrent.carrental.model.Permission> resolvePermissions(
            Role role, java.util.Set<com.mvrent.carrental.model.Permission> requested) {
        // Admins implicitly hold every permission; staff get exactly what's granted.
        if (role == Role.ADMIN) return java.util.EnumSet.allOf(com.mvrent.carrental.model.Permission.class);
        return (requested == null || requested.isEmpty())
                ? java.util.EnumSet.noneOf(com.mvrent.carrental.model.Permission.class)
                : java.util.EnumSet.copyOf(requested);
    }

    /** Create a walk-in / counter customer record on the fly (admin booking). */
    @org.springframework.transaction.annotation.Transactional
    public User createWalkInCustomer(String fullName, String phone, String email) {
        User u = new User();
        String base = (fullName == null ? "guest" : fullName.trim().toLowerCase().replaceAll("[^a-z0-9]+", "")).trim();
        if (base.isEmpty()) base = "guest";
        String username = base;
        int n = 1;
        while (userRepository.existsByUsername(username)) username = base + (++n);
        u.setUsername(username);
        u.setFullName(fullName == null || fullName.isBlank() ? "Walk-in Customer" : fullName.trim());
        if (email != null && !email.isBlank() && !userRepository.existsByEmail(email.trim())) {
            u.setEmail(email.trim());
        } else {
            u.setEmail(username + "@walkin.local");
        }
        u.setPhone(phone);
        u.setRole(Role.CUSTOMER);
        u.setEnabled(true);
        u.setPassword(passwordEncoder.encode(java.util.UUID.randomUUID().toString()));
        return userRepository.save(u);
    }

    /** Change the user's password after verifying the current one. */
    @org.springframework.transaction.annotation.Transactional
    public void changePassword(String username, String currentPassword, String newPassword) {
        User user = findByUsername(username);
        if (currentPassword == null || !passwordEncoder.matches(currentPassword, user.getPassword())) {
            throw new IllegalArgumentException("Current password is incorrect");
        }
        if (newPassword == null || newPassword.length() < 6) {
            throw new IllegalArgumentException("New password must be at least 6 characters");
        }
        user.setPassword(passwordEncoder.encode(newPassword));
        userRepository.save(user);
    }
}
