PlatformService.kt

package com.distasilucas.cryptobalancetracker.service

import com.distasilucas.cryptobalancetracker.constants.ALL_PLATFORMS_CACHE
import com.distasilucas.cryptobalancetracker.constants.DUPLICATED_PLATFORM
import com.distasilucas.cryptobalancetracker.constants.PLATFORMS_PLATFORMS_IDS_CACHE
import com.distasilucas.cryptobalancetracker.constants.PLATFORM_ID_NOT_FOUND
import com.distasilucas.cryptobalancetracker.constants.PLATFORM_PLATFORM_ID_CACHE
import com.distasilucas.cryptobalancetracker.entity.Platform
import com.distasilucas.cryptobalancetracker.model.request.platform.PlatformRequest
import com.distasilucas.cryptobalancetracker.repository.PlatformRepository
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.cache.annotation.Cacheable
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Scope
import org.springframework.context.annotation.ScopedProxyMode
import org.springframework.stereotype.Service

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PlatformService(
  private val platformRepository: PlatformRepository,
  @Lazy private val userCryptoService: UserCryptoService,
  private val cacheService: CacheService,
  private val _platformService: PlatformService?
) {

  private val logger = KotlinLogging.logger { }

  fun countPlatforms() = platformRepository.count()

  @Cacheable(cacheNames = [PLATFORM_PLATFORM_ID_CACHE], key = "#platformId")
  fun retrievePlatformById(platformId: String): Platform {
    logger.info { "Retrieving platformId $platformId" }

    return platformRepository.findById(platformId)
      .orElseThrow { PlatformNotFoundException(PLATFORM_ID_NOT_FOUND.format(platformId)) }
  }

  @Cacheable(cacheNames = [ALL_PLATFORMS_CACHE])
  fun retrieveAllPlatforms(): List<Platform> {
    logger.info { "Retrieving all platforms" }

    return platformRepository.findAll()
  }

  @Cacheable(cacheNames = [PLATFORMS_PLATFORMS_IDS_CACHE], key = "#ids")
  fun findAllByIds(ids: Collection<String>): List<Platform> {
    logger.info { "Retrieving platforms for ids $ids" }

    return platformRepository.findAllByIdIn(ids)
  }

  fun savePlatform(platformRequest: PlatformRequest): Platform {
    validatePlatformNotExists(platformRequest.name!!)

    val platform = platformRequest.toEntity()
    val platformEntity = platformRepository.save(platform)
    cacheService.invalidate(CacheType.PLATFORMS_CACHES)

    logger.info { "Saved platform $platformEntity" }

    return platformEntity
  }

  fun updatePlatform(platformId: String, platformRequest: PlatformRequest): Platform {
    validatePlatformNotExists(platformRequest.name!!)

    val existingPlatform = _platformService!!.retrievePlatformById(platformId)
    val updatedPlatform = platformRequest.toEntity(id = existingPlatform.id)

    platformRepository.save(updatedPlatform)
    cacheService.invalidate(CacheType.PLATFORMS_CACHES, CacheType.USER_CRYPTOS_CACHES, CacheType.INSIGHTS_CACHES)
    logger.info { "Updated platform. Before: $existingPlatform  | After: $updatedPlatform" }

    return updatedPlatform
  }

  fun deletePlatform(platformId: String) {
    val platform = _platformService!!.retrievePlatformById(platformId)
    val userCryptosToDelete = userCryptoService.findAllByPlatformId(platform.id)

    userCryptoService.deleteUserCryptos(userCryptosToDelete)
    platformRepository.delete(platform)
    cacheService.invalidate(CacheType.PLATFORMS_CACHES, CacheType.INSIGHTS_CACHES)

    logger.info { "Deleted platform ${platform.name}" }
  }

  private fun validatePlatformNotExists(platformName: String) {
    val existingPlatform: Platform? = platformRepository.findByName(platformName.uppercase())

    if (existingPlatform != null) {
      throw DuplicatedPlatformException(DUPLICATED_PLATFORM.format(existingPlatform.name))
    }
  }
}

class PlatformNotFoundException(message: String) : RuntimeException(message)

class DuplicatedPlatformException(message: String) : RuntimeException(message)