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.Errorcode
import com.securityc4po.api.configuration.error.handler.TransactionInterruptedException
import com.securityc4po.api.extensions.getLoggerFor
import com.securityc4po.api.pentest.comment.CommentRepository
import com.securityc4po.api.pentest.finding.FindingRepository
import com.securityc4po.api.project.Project
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

@Service
class PentestDeletionService(
    private val pentestRepository: PentestRepository,
    private val findingRepository: FindingRepository,
    private val commentRepository: CommentRepository
) {
    var logger = getLoggerFor<PentestDeletionService>()

    @SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
    fun deletePentestsAndAllAssociatedFindingsAndComments(project: Project): Flux<Project> {
        val pentestIds = project.projectPentests.map { it.pentestId }
        return pentestRepository.findPentestsByIds(pentestIds).collectList()
            .flatMapMany { pentestEntityList -> Flux.fromIterable(pentestEntityList) }.flatMap { pentestEntity ->
            this.pentestRepository.deletePentestById(pentestEntity.data.id).flatMap {
                // Delete all associated findings of the pentest
                val findingsDeletionResult =
                    this.findingRepository.deleteFindingsByIds(pentestEntity.data.findingIds).onErrorMap {
                        TransactionInterruptedException(
                            "Finding could not be deleted.",
                            Errorcode.FindingDeletionFailed
                        )
                    }
                // Delete all associated comments of the pentest
                val commentsDeletionResult =
                    this.commentRepository.deleteCommentsByIds(pentestEntity.data.commentIds).onErrorMap {
                        TransactionInterruptedException(
                            "Comments could not be deleted.",
                            Errorcode.CommentDeletionFailed
                        )
                    }
                // Hack to map result together
                findingsDeletionResult.flatMap {
                    commentsDeletionResult.flatMap {
                        Mono.just(project)
                    }
                }

            }.onErrorMap {
                TransactionInterruptedException(
                    "Pentest deletion failed",
                    Errorcode.PentestDeletionFailed
                )
            }
        }.flatMap {
            Mono.just(project)
        }
    }
}
