package kr.lunaticbum.back.lun.model

import kr.lunaticbum.back.lun.utils.LogService
import lombok.*
import org.bson.BsonType
import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonRepresentation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.Query
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import java.time.Duration


@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "User")
class User {

    @BsonId
    @BsonRepresentation(BsonType.OBJECT_ID)
    var userId: String? = null

    @Id
    var user_id: String? = null
    var user_pw: String? = null
    var user_pw_check: String? = null

    var user_email: String? = null
    @CreatedDate
    var user_join: Long = 0L

//    var user_name: String? = null
    var isAccept : String? = null
    var isAdmin : String? = null

    var remeberMe : Boolean? = false

    fun checkValid() : Boolean {
        if (
            ((user_id?.length ?: 0) > 5) &&
            ((user_pw?.length ?: 0) > 9) &&
            ((user_email?.length ?: 0) > 8) &&
            user_email?.contains("@") == true &&
            user_email?.contains(".") == true &&
            (user_email?.split("@")?.get(0)?.length ?: 0) > 1 &&
            (user_email?.split("@")?.get(1)?.length ?: 0) > 1 &&
            (user_email?.split(".")?.get(1)?.length ?: 0) > 1
        ) {
            if (user_id.equals("lun_admin") || user_id.equals("lunaticbum")){
                isAdmin = "Y"
            } else {
                isAccept = "N"
                isAdmin = "N"
            }
            return true
        }
        return false
    }

    /**
     * 비밀번호를 암호화
     * @param passwordEncoder 암호화 할 인코더 클래스
     * @return 변경된 유저 Entity
     */
    fun hashPassword(passwordEncoder: PasswordEncoder): User {
        this.user_pw = passwordEncoder.encode(this.user_pw)
        return this
    }

    /**
     * 비밀번호 확인
     * @param plainPassword 암호화 이전의 비밀번호
     * @param passwordEncoder 암호화에 사용된 클래스
     * @return true | false
     */
    fun checkPassword(plainPassword: String?, passwordEncoder: PasswordEncoder): Boolean {
        return passwordEncoder.matches(plainPassword, this.user_pw)
    }

    fun getIdentifier(): String? {
        return user_id
    }

    fun getRole(): UserRole {
        if ("Y".equals(isAdmin)) return UserRole.ADMIN
        if ("Y".equals(isAccept)) return UserRole.WRITE
        return UserRole.READ
    }
}

@Getter
@RequiredArgsConstructor
enum class Role(key : String, description: String) {
    USER("ROLE_USER", "일반사용자"),
    ADMIN("ROLE_ADMIN", "일반관리자");

    private val key: String? = null
    private val title: String? = null
}

@Repository
interface UserRepository : ReactiveMongoRepository<User, String> {
    @Query("{user_id :?0}")
    override fun findById(user_id: String): Mono<User>

//    @Query("{user_email :?0}")
//    fun findByEmail(user_email: String): Mono<User>


    fun save(user: User): Mono<User>
}

enum class UserRole {
    NOT_REGISTERED,READ,WRITE,ADMIN
}
interface UserService {
    fun findById(id: String): Mono<User>?
//    fun findByEmail(id: String): Mono<User>?
}

@Service
class UserManager(
    private val passwordEncoder: PasswordEncoder
) : UserService , UserDetailsService {
    @Autowired
    private lateinit var logService: LogService

    @Autowired
    private lateinit var userRepository: UserRepository



//    override fun findByEmail(id: String): Mono<User>? {
//        return userRepository.findByEmail(id)
//    }

    override fun findById(id: String): Mono<User>? {
        return userRepository.findById(id)
    }



    fun save(user: User): Mono<User> {
        println("saved user before ${user}")
        user.hashPassword(passwordEncoder)
        return userRepository.save(user)
    }

    fun isCorrectUser(user: User, password : String) : Boolean {
        return user.checkPassword(password,passwordEncoder)
    }


    override fun loadUserByUsername(username: String?): UserDetails {
        logService.log("username ${username}")
        var user = findById(username!!)?.blockOptional(Duration.ofMillis(5000L))?.get() ?: User()
//        user.hashPassword(passwordEncoder)
        return org.springframework.security.core.userdetails.User.builder()
            .username(user.user_id ?: "")
            .password(user.user_pw)
            .roles(if ("Y".equals(user.isAdmin)) Role.ADMIN.name else {Role.USER.name}).build()
    }
}