package llc.arma.fleet.monitor.security

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.time.OffsetDateTime
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

@Component
class AuthTokenStore(
    @Value("\${app.auth.access-token-ttl-minutes:720}") private val accessTokenTtlMinutes: Long,
    @Value("\${app.auth.refresh-token-ttl-minutes:10080}")
    private val refreshTokenTtlMinutes: Long,
) {
    private data class TokenRecord(
        val userId: Long,
        val expiresAt: OffsetDateTime,
    )

    data class AuthSession(
        val userId: Long,
        val accessToken: String,
        val refreshToken: String,
    )

    private val accessTokens = ConcurrentHashMap<String, TokenRecord>()
    private val refreshTokens = ConcurrentHashMap<String, TokenRecord>()

    fun createToken(userId: Long): String {
        val session = createSession(userId)

        return session.accessToken
    }

    fun createSession(userId: Long): AuthSession {
        val accessToken = generateToken()
        val refreshToken = generateToken()
        val now = OffsetDateTime.now()

        accessTokens[accessToken] = TokenRecord(
            userId = userId,
            expiresAt = now.plusMinutes(accessTokenTtlMinutes),
        )
        refreshTokens[refreshToken] = TokenRecord(
            userId = userId,
            expiresAt = now.plusMinutes(refreshTokenTtlMinutes),
        )

        return AuthSession(
            userId = userId,
            accessToken = accessToken,
            refreshToken = refreshToken,
        )
    }

    fun refreshSession(refreshToken: String?): AuthSession? {
        val record = resolveToken(refreshTokens, refreshToken) ?: return null

        if (!refreshToken.isNullOrBlank()) {
            refreshTokens.remove(refreshToken)
        }

        return createSession(record.userId)
    }

    fun resolveUserId(token: String?): Long? {
        return resolveToken(accessTokens, token)?.userId
    }

    fun revoke(token: String?) {
        if (!token.isNullOrBlank()) {
            accessTokens.remove(token)
            refreshTokens.remove(token)
        }
    }

    private fun resolveToken(
        store: ConcurrentHashMap<String, TokenRecord>,
        token: String?,
    ): TokenRecord? {
        if (token.isNullOrBlank()) return null
        val record = store[token] ?: return null

        if (record.expiresAt.isBefore(OffsetDateTime.now())) {
            store.remove(token)
            return null
        }

        return record
    }

    private fun generateToken(): String {
        return UUID.randomUUID().toString().replace("-", "")
    }
}
