package de.cannamanage.api.integration;

import de.cannamanage.api.dto.auth.LoginRequest;
import de.cannamanage.api.dto.auth.LoginResponse;
import de.cannamanage.api.dto.member.CreateMemberRequest;
import de.cannamanage.api.dto.member.MemberResponse;
import de.cannamanage.api.dto.stock.BatchResponse;
import de.cannamanage.api.dto.stock.CreateBatchRequest;
import de.cannamanage.domain.entity.Club;
import de.cannamanage.domain.entity.Member;
import de.cannamanage.domain.entity.User;
import de.cannamanage.domain.enums.ClubStatus;
import de.cannamanage.domain.enums.UserRole;
import de.cannamanage.service.repository.ClubRepository;
import de.cannamanage.service.repository.MemberRepository;
import de.cannamanage.service.repository.UserRepository;
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.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.web.client.RestClient;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;

/**
 * Base class for integration tests using Testcontainers PostgreSQL.
 * Uses RestClient (Spring Boot 4 — TestRestTemplate was removed in Boot 4).
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
@ActiveProfiles("integration")
public abstract class AbstractIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
            .withDatabaseName("cannamanage_test")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @LocalServerPort
    protected int port;

    @Autowired
    protected UserRepository userRepository;

    @Autowired
    protected ClubRepository clubRepository;

    @Autowired
    protected MemberRepository memberRepository;

    @Autowired
    protected PasswordEncoder passwordEncoder;

    /**
     * Creates a RestClient configured with the test server's base URL.
     * Configured to NOT throw on 4xx/5xx responses (so tests can assert status codes).
     */
    protected RestClient restClient() {
        return RestClient.builder()
                .baseUrl("http://localhost:" + port)
                .defaultStatusHandler(org.springframework.http.HttpStatusCode::isError, (req, res) -> {
                    // Don't throw — let tests inspect status codes directly
                })
                .build();
    }

    // --- Auth helper methods ---

    /**
     * Logs in with given credentials and returns the full LoginResponse.
     */
    protected LoginResponse login(String email, String password) {
        return restClient().post()
                .uri("/api/v1/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .body(new LoginRequest(email, password))
                .retrieve()
                .body(LoginResponse.class);
    }

    /**
     * Convenience: login and return just the access token.
     */
    protected String getAccessToken(String email, String password) {
        return login(email, password).accessToken();
    }

    // --- Test data creation helpers ---

    /**
     * Creates a club (tenant) and returns its ID.
     */
    protected UUID createTestClub(String name) {
        Club club = new Club();
        club.setName(name);
        club.setStatus(ClubStatus.ACTIVE);
        club.setMaxMembers(500);
        club.setMaxPreventionOfficers(3);
        club = clubRepository.save(club);
        return club.getId();
    }

    /**
     * Creates an admin user for the given tenant and returns the User entity.
     */
    protected User createAdminUser(UUID tenantId, String email, String password) {
        User user = new User();
        user.setTenantId(tenantId);
        user.setEmail(email);
        user.setPasswordHash(passwordEncoder.encode(password));
        user.setRole(UserRole.ROLE_ADMIN);
        user.setActive(true);
        return userRepository.save(user);
    }

    /**
     * Creates a member user for the portal (ROLE_MEMBER) linked to a Member entity.
     */
    protected User createMemberUser(UUID tenantId, UUID memberId, String email, String password) {
        User user = new User();
        user.setTenantId(tenantId);
        user.setMemberId(memberId);
        user.setEmail(email);
        user.setPasswordHash(passwordEncoder.encode(password));
        user.setRole(UserRole.ROLE_MEMBER);
        user.setActive(true);
        return userRepository.save(user);
    }

    /**
     * Creates a Member entity via API (requires admin token).
     */
    protected MemberResponse createTestMember(String adminToken, String firstName, String lastName,
                                              String email, LocalDate dateOfBirth) {
        CreateMemberRequest request = new CreateMemberRequest(
                firstName, lastName, email, dateOfBirth,
                LocalDate.now(), "M-" + UUID.randomUUID().toString().substring(0, 8));
        return restClient().post()
                .uri("/api/v1/members")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(MemberResponse.class);
    }

    /**
     * Creates a Batch entity via API (requires admin token).
     */
    protected BatchResponse createTestBatch(String adminToken, UUID strainId, BigDecimal quantity, String batchCode) {
        CreateBatchRequest request = new CreateBatchRequest(strainId, quantity, LocalDate.now(), batchCode);
        return restClient().post()
                .uri("/api/v1/stock/batches")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(request)
                .retrieve()
                .body(BatchResponse.class);
    }

    /**
     * Creates a Member entity directly in the DB (bypassing API / tenant filter).
     */
    protected Member createMemberDirectly(UUID tenantId, String firstName, String lastName,
                                          String email, LocalDate dateOfBirth) {
        Member member = new Member();
        member.setTenantId(tenantId);
        member.setClubId(tenantId);
        member.setFirstName(firstName);
        member.setLastName(lastName);
        member.setEmail(email);
        member.setDateOfBirth(dateOfBirth);
        member.setMembershipDate(LocalDate.now());
        member.setMembershipNumber("M-" + UUID.randomUUID().toString().substring(0, 8));
        member.setUnder21(java.time.Period.between(dateOfBirth, LocalDate.now()).getYears() < 21);
        return memberRepository.save(member);
    }
}
