package de.cannamanage.api.controller;

import de.cannamanage.api.dto.auth.LoginRequest;
import de.cannamanage.api.dto.auth.LoginResponse;
import de.cannamanage.api.dto.auth.RefreshRequest;
import de.cannamanage.domain.entity.TenantContext;
import de.cannamanage.domain.entity.User;
import de.cannamanage.domain.enums.UserRole;
import de.cannamanage.service.repository.UserRepository;
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.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClient;

import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
 * Integration tests for {@link AuthController}.
 * Boots the full Spring context with H2 in-memory DB and a real HTTP server.
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class AuthControllerIntegrationTest {

    @LocalServerPort
    private int port;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    private RestClient restClient;

    private static final UUID TENANT_ID = UUID.fromString("00000000-0000-0000-0000-000000000001");
    private static final String TEST_EMAIL = "admin@test.club";
    private static final String TEST_PASSWORD = "SecurePass123!";

    @BeforeEach
    void setUp() {
        restClient = RestClient.builder()
                .baseUrl("http://localhost:" + port)
                .build();

        userRepository.deleteAll();

        // Set TenantContext so @PrePersist can pick up tenantId
        TenantContext.setCurrentTenant(TENANT_ID);

        User user = new User();
        user.setEmail(TEST_EMAIL);
        user.setPasswordHash(passwordEncoder.encode(TEST_PASSWORD));
        user.setRole(UserRole.ROLE_ADMIN);
        user.setActive(true);
        userRepository.saveAndFlush(user);

        TenantContext.clear();
    }

    @Test
    @DisplayName("POST /api/v1/auth/login — valid credentials returns tokens")
    void login_withValidCredentials_returnsTokens() throws Exception {
        LoginRequest request = new LoginRequest(TEST_EMAIL, TEST_PASSWORD);

        LoginResponse response = restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(LoginResponse.class);

        assertThat(response).isNotNull();
        assertThat(response.accessToken()).isNotBlank();
        assertThat(response.refreshToken()).isNotBlank();
        assertThat(response.expiresIn()).isEqualTo(3600L);
        assertThat(response.role()).isEqualTo("ADMIN");
    }

    @Test
    @DisplayName("POST /api/v1/auth/login — wrong password returns 401")
    void login_withWrongPassword_returns401() {
        LoginRequest request = new LoginRequest(TEST_EMAIL, "WrongPassword!");

        assertThatThrownBy(() -> restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(String.class))
                .isInstanceOf(HttpClientErrorException.Unauthorized.class);
    }

    @Test
    @DisplayName("POST /api/v1/auth/login — unknown email returns 401")
    void login_withUnknownEmail_returns401() {
        LoginRequest request = new LoginRequest("nobody@test.club", TEST_PASSWORD);

        assertThatThrownBy(() -> restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(String.class))
                .isInstanceOf(HttpClientErrorException.Unauthorized.class);
    }

    @Test
    @DisplayName("POST /api/v1/auth/login — disabled user returns 401")
    void login_withDisabledUser_returns401() {
        // Disable the test user
        TenantContext.setCurrentTenant(TENANT_ID);
        User user = userRepository.findByEmail(TEST_EMAIL).orElseThrow();
        user.setActive(false);
        userRepository.saveAndFlush(user);
        TenantContext.clear();

        LoginRequest request = new LoginRequest(TEST_EMAIL, TEST_PASSWORD);

        assertThatThrownBy(() -> restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(String.class))
                .isInstanceOf(HttpClientErrorException.Unauthorized.class);
    }

    @Test
    @DisplayName("POST /api/v1/auth/login — missing email returns 400")
    void login_withMissingEmail_returns400() {
        assertThatThrownBy(() -> restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body("{\"password\": \"test123\"}")
                .retrieve()
                .body(String.class))
                .isInstanceOf(HttpClientErrorException.BadRequest.class);
    }

    @Test
    @DisplayName("POST /api/v1/auth/refresh — valid refresh token returns new token pair")
    void refresh_withValidToken_returnsNewTokens() throws Exception {
        // First login to get a refresh token
        LoginRequest loginRequest = new LoginRequest(TEST_EMAIL, TEST_PASSWORD);

        LoginResponse loginResponse = restClient.post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(loginRequest)
                .retrieve()
                .body(LoginResponse.class);

        assertThat(loginResponse).isNotNull();
        String refreshToken = loginResponse.refreshToken();

        // Use the refresh token
        RefreshRequest refreshRequest = new RefreshRequest(refreshToken);

        LoginResponse refreshResponse = restClient.post()
                .uri("/api/v1/auth/refresh")
                .contentType(MediaType.APPLICATION_JSON)
                .body(refreshRequest)
                .retrieve()
                .body(LoginResponse.class);

        assertThat(refreshResponse).isNotNull();
        assertThat(refreshResponse.accessToken()).isNotBlank();
        assertThat(refreshResponse.refreshToken()).isNotBlank();
        assertThat(refreshResponse.role()).isEqualTo("ADMIN");
    }

    @Test
    @DisplayName("POST /api/v1/auth/refresh — invalid token returns 401")
    void refresh_withInvalidToken_returns401() {
        RefreshRequest request = new RefreshRequest("invalid.jwt.token");

        assertThatThrownBy(() -> restClient.post()
                .uri("/api/v1/auth/refresh")
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(String.class))
                .isInstanceOf(HttpClientErrorException.Unauthorized.class);
    }
}
