package space.nstart.pcp_tgu_service.service

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoInteractions
import org.mockito.Mockito.`when`
import org.springframework.http.HttpStatus
import org.springframework.web.server.ResponseStatusException
import space.nstart.pcp_tgu_service.domain.PlanDecision
import space.nstart.pcp_tgu_service.dto.PlanDecisionMessage
import space.nstart.pcp_tgu_service.entity.PlanIssueAttemptEntity
import space.nstart.pcp_tgu_service.entity.PlanIssueAttemptStatus
import space.nstart.pcp_tgu_service.entity.PlannedPlanEntity
import space.nstart.pcp_tgu_service.entity.PlannedPlanStatus
import space.nstart.pcp_tgu_service.repository.PlanIssueAttemptRepository
import space.nstart.pcp_tgu_service.repository.PlannedPlanRepository
import java.time.LocalDateTime
import java.util.Optional
import java.util.UUID

class TestPlanDecisionServiceTest {

    @Test
    fun `generate decision builds message for latest attempt and delegates to use case`() {
        val plan = plannedPlan()
        val attempt = attempt(plan.planId, PlanIssueAttemptStatus.STARTED)
        val handlePlanDecisionUseCase = mock(HandlePlanDecisionUseCase::class.java)
        val service = service(plan, attempt, handlePlanDecisionUseCase)

        val message = service.generateDecision(plan.planId, PlanDecision.ACCEPTED, "manual accept")

        assertNotNull(message.eventId)
        assertEquals(plan.spacecraftId, message.spacecraftId)
        assertEquals(plan.planId, message.planId)
        assertEquals(attempt.attemptId, message.attemptId)
        assertEquals(PlanDecision.ACCEPTED, message.decision)
        assertEquals("manual accept", message.reason)
        verify(handlePlanDecisionUseCase).handle(message)
    }

    @Test
    fun `generate decision rejects plan without any attempt`() {
        val plan = plannedPlan()
        val handlePlanDecisionUseCase = mock(HandlePlanDecisionUseCase::class.java)
        val service = service(plan, attempt = null, handlePlanDecisionUseCase)

        val exception = assertThrows(ResponseStatusException::class.java) {
            service.generateDecision(plan.planId, PlanDecision.REJECTED, null)
        }

        assertEquals(HttpStatus.CONFLICT, exception.statusCode)
        verifyNoInteractions(handlePlanDecisionUseCase)
    }

    private fun service(
        plan: PlannedPlanEntity,
        attempt: PlanIssueAttemptEntity?,
        handlePlanDecisionUseCase: HandlePlanDecisionUseCase
    ): TestPlanDecisionService {
        val plannedPlanRepository = mock(PlannedPlanRepository::class.java)
        val planIssueAttemptRepository = mock(PlanIssueAttemptRepository::class.java)

        `when`(plannedPlanRepository.findById(plan.planId)).thenReturn(Optional.of(plan))
        `when`(planIssueAttemptRepository.findTopByPlanIdOrderByAttemptNoDesc(plan.planId)).thenReturn(attempt)

        return TestPlanDecisionService(
            plannedPlanRepository = plannedPlanRepository,
            planIssueAttemptRepository = planIssueAttemptRepository,
            handlePlanDecisionUseCase = handlePlanDecisionUseCase
        )
    }

    private fun plannedPlan(): PlannedPlanEntity =
        PlannedPlanEntity(
            planId = UUID.randomUUID(),
            spacecraftId = "25544",
            startTime = LocalDateTime.now().plusHours(1),
            endTime = LocalDateTime.now().plusHours(2),
            kppId = "KPP-1",
            status = PlannedPlanStatus.WAITING_DECISION
        )

    private fun attempt(planId: UUID, status: PlanIssueAttemptStatus): PlanIssueAttemptEntity =
        PlanIssueAttemptEntity(
            attemptId = UUID.randomUUID(),
            planId = planId,
            attemptNo = 1,
            status = status
        )
}
