package llc.arma.fleet.monitor.domain.service

import llc.arma.fleet.monitor.data.entity.SortDirection
import llc.arma.fleet.monitor.data.entity.UserEntity
import llc.arma.fleet.monitor.data.entity.UserRole
import llc.arma.fleet.monitor.data.entity.UserSortField
import llc.arma.fleet.monitor.data.repository.UserRepository
import llc.arma.fleet.monitor.presentation.model.CreateUserPayload
import llc.arma.fleet.monitor.presentation.model.UpdateUserPayload
import llc.arma.fleet.monitor.presentation.model.UsersPage
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service

@Service
class UserService(
    private val userRepository: UserRepository,
    private val passwordEncoder: PasswordEncoder,
) {
    fun getUsers(
        page: Int,
        pageSize: Int?,
        query: String?,
        roles: List<UserRole>?,
        sortDirection: SortDirection,
        sortField: UserSortField,
    ): UsersPage {
        val pageable = PageRequest.of(
            page.coerceAtLeast(0),
            (pageSize ?: 20).coerceIn(1, 100),
            Sort.by(
                if (sortDirection == SortDirection.ASC) Sort.Direction.ASC else Sort.Direction.DESC,
                when (sortField) {
                    UserSortField.ID -> "id"
                    UserSortField.NAME -> "lastName"
                    UserSortField.DATE -> "createdAt"
                },
            ),
        )

        val allowedRoles = roles?.takeIf { it.isNotEmpty() }
        val normalizedQuery = query?.trim()?.takeIf { it.isNotBlank() }

        val result = when {
            normalizedQuery != null && allowedRoles != null -> {
                userRepository.searchByQueryAndRoleIn(normalizedQuery, allowedRoles, pageable)
            }

            normalizedQuery != null -> {
                userRepository.findByLastNameContainingIgnoreCaseOrFirstNameContainingIgnoreCaseOrEmailContainingIgnoreCaseOrLoginContainingIgnoreCase(
                    normalizedQuery,
                    pageable,
                )
            }

            allowedRoles != null -> {
                userRepository.findByRoleIn(allowedRoles, pageable)
            }

            else -> {
                userRepository.findAll(pageable)
            }
        }

        return UsersPage(
            totalPages = result.totalPages,
            totalElements = result.totalElements,
            page = result.content,
        )
    }

    fun getUser(id: Long): UserEntity? = userRepository.findById(id).orElse(null)

    fun createUser(payload: CreateUserPayload): UserEntity {
        require(!userRepository.existsByEmail(payload.email)) { "Email уже используется" }
        require(!userRepository.existsByLogin(payload.login)) { "Логин уже используется" }

        return userRepository.save(
            UserEntity(
                lastName = payload.lastName,
                firstName = payload.firstName,
                middleName = payload.middleName,
                email = payload.email,
                login = payload.login,
                passwordHash = encodePassword(payload.password),
                role = payload.role,
            ),
        )
    }

    private fun encodePassword(password: String): String =
        passwordEncoder.encode(password) ?: error("Password encoder returned null")

    fun updateUser(id: Long, payload: UpdateUserPayload): UserEntity {
        val user = userRepository.findById(id).orElseThrow { IllegalArgumentException("Пользователь не найден") }

        payload.lastName?.let { user.lastName = it }
        payload.firstName?.let { user.firstName = it }
        if (payload.middleName != null) user.middleName = payload.middleName
        payload.email?.let {
            require(!userRepository.existsByEmailAndIdNot(it, id)) { "Email уже используется" }
            user.email = it
        }
        payload.login?.let {
            require(!userRepository.existsByLoginAndIdNot(it, id)) { "Логин уже используется" }
            user.login = it
        }
        payload.password?.let { user.passwordHash = encodePassword(it) }
        payload.role?.let { user.role = it }
        payload.active?.let { user.active = it }

        return userRepository.save(user)
    }

    fun deleteUser(id: Long): Boolean {
        if (!userRepository.existsById(id)) return false
        userRepository.deleteById(id)
        return true
    }
}
