package com.securityc4po.api.pentest

import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.configuration.error.handler.EntityNotFoundException
import com.securityc4po.api.configuration.error.handler.Errorcode
import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.pentest.comment.CommentService
import com.securityc4po.api.pentest.finding.Finding
import com.securityc4po.api.pentest.finding.FindingService
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import reactor.kotlin.core.publisher.switchIfEmpty
import reactor.kotlin.core.publisher.toMono

@Service
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
class PentestReportService(
    private val pentestRepository: PentestRepository,
    private val findingService: FindingService,
    private val commentService: CommentService
) {

    var logger = getLoggerFor<PentestReportService>()

    /**
     * Get [CompletedPentest]s by pentestId
     *
     * @return [CompletedPentest]
     */
    fun getCompletedPentest(pentestId: String): Mono<CompletedPentest> {
        return pentestRepository.findPentestById(pentestId).publishOn(Schedulers.boundedElastic())
            .flatMap { pentestEntity ->
                val completedPentest = pentestEntity.toCompletedPentest()
                // Add all findings to completed Pentest
                this.findingService.getFindingsByIds(pentestEntity.data.findingIds).flatMap { listOfFindings ->
                    completedPentest.findings.addAll(listOfFindings)
                    // Add all comments to completed Pentest
                    this.commentService.getCommentsByIds(pentestEntity.data.commentIds).map { listOfComments ->
                        completedPentest.comments.addAll(listOfComments)
                        // Return completed Pentest
                        return@map completedPentest
                    }
                }
            }.switchIfEmpty {
                logger.warn("Pentest for id $pentestId not found. Collecting pentest information not possible.")
                val msg = "Pentest for id $pentestId not found."
                val ex = EntityNotFoundException(msg, Errorcode.PentestNotFound)
                throw ex
            }
    }
}