package com.saas.subscription.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.saas.subscription.dto.request.CreateSubscriptionPlanRequest;
import com.saas.subscription.dto.response.SubscriptionPlanResponse;
import com.saas.subscription.service.SubscriptionPlanService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import java.math.BigDecimal;
import java.util.List;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

/**
 * Integration Tests for AdminSubscriptionPlanController
 */
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("AdminSubscriptionPlanController Integration Tests")
class AdminSubscriptionPlanControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    private SubscriptionPlanService planService;

    private CreateSubscriptionPlanRequest testRequest;
    private SubscriptionPlanResponse testResponse;

    @BeforeEach
    void setUp() {
        testRequest = CreateSubscriptionPlanRequest.builder()
            .name("PRO")
            .description("Professional plan for growing businesses")
            .monthlyPrice(new BigDecimal("99.00"))
            .annualPrice(new BigDecimal("990.00"))
            .maxConcurrentCalls(10)
            .maxAiMinutes(5000)
            .maxAiAgents(5)
            .maxUsers(50)
            .maxPhoneNumbers(10)
            .storageQuotaGb(100)
            .build();

        testResponse = SubscriptionPlanResponse.builder()
            .id(1L)
            .name("PRO")
            .description("Professional plan for growing businesses")
            .monthlyPrice(new BigDecimal("99.00"))
            .isActive(true)
            .build();
    }

    @Test
    @DisplayName("Should return 401 when not authenticated")
    void testCreatePlanUnauthorized() throws Exception {
        mockMvc.perform(post("/api/admin/subscription-plans")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(testRequest)))
            .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    @DisplayName("Should return 403 when user lacks SYSTEM_ADMIN role")
    void testCreatePlanForbidden() throws Exception {
        mockMvc.perform(post("/api/admin/subscription-plans")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(testRequest)))
            .andExpect(status().isForbidden());
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should create subscription plan successfully")
    void testCreatePlanSuccess() throws Exception {
        // Arrange
        when(planService.createPlan(any(CreateSubscriptionPlanRequest.class)))
            .thenReturn(testResponse);

        // Act & Assert
        mockMvc.perform(post("/api/admin/subscription-plans")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(testRequest)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.success").value(true))
            .andExpect(jsonPath("$.data.id").value(1L))
            .andExpect(jsonPath("$.data.name").value("PRO"))
            .andExpect(jsonPath("$.data.monthly_price").value(99.00));
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should return 400 when validation fails")
    void testCreatePlanValidationError() throws Exception {
        // Arrange - Invalid request (missing required fields)
        CreateSubscriptionPlanRequest invalidRequest = CreateSubscriptionPlanRequest.builder()
            .name("") // Empty name
            .build();

        // Act & Assert
        mockMvc.perform(post("/api/admin/subscription-plans")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(invalidRequest)))
            .andExpect(status().isBadRequest());
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should get all active plans")
    void testGetAllPlans() throws Exception {
        // Arrange
        when(planService.getAllActivePlans()).thenReturn(List.of(testResponse));

        // Act & Assert
        mockMvc.perform(get("/api/admin/subscription-plans")
                .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.success").value(true))
            .andExpect(jsonPath("$.data").isArray())
            .andExpect(jsonPath("$.data[0].name").value("PRO"));
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should get plan by ID")
    void testGetPlanById() throws Exception {
        // Arrange
        when(planService.getPlan(1L)).thenReturn(testResponse);

        // Act & Assert
        mockMvc.perform(get("/api/admin/subscription-plans/1")
                .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.success").value(true))
            .andExpect(jsonPath("$.data.id").value(1L))
            .andExpect(jsonPath("$.data.name").value("PRO"));
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should update plan successfully")
    void testUpdatePlan() throws Exception {
        // Arrange
        when(planService.updatePlan(eq(1L), any(CreateSubscriptionPlanRequest.class)))
            .thenReturn(testResponse);

        // Act & Assert
        mockMvc.perform(put("/api/admin/subscription-plans/1")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(testRequest)))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.success").value(true))
            .andExpect(jsonPath("$.data.name").value("PRO"));
    }

    @Test
    @WithMockUser(roles = "SYSTEM_ADMIN")
    @DisplayName("Should delete plan successfully")
    void testDeletePlan() throws Exception {
        // Act & Assert
        mockMvc.perform(delete("/api/admin/subscription-plans/1")
                .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isNoContent());
    }
}
