package de.cannamanage.api.integration;

import de.cannamanage.api.dto.auth.LoginResponse;
import de.cannamanage.api.dto.auth.SetPasswordRequest;
import de.cannamanage.api.dto.staff.CreateStaffRequest;
import de.cannamanage.api.dto.staff.StaffResponse;
import de.cannamanage.api.dto.staff.UpdateStaffRequest;
import de.cannamanage.domain.entity.InviteToken;
import de.cannamanage.domain.enums.StaffPermission;
import de.cannamanage.service.repository.InviteTokenRepository;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import java.util.List;
import java.util.Set;
import java.util.UUID;

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

/**
 * Integration test: Staff invite → activate → permission check flow.
 */
class StaffPermissionIntegrationTest extends AbstractIntegrationTest {

    @Autowired
    private InviteTokenRepository inviteTokenRepository;

    private UUID tenantId;
    private String adminToken;

    @BeforeEach
    void setUp() {
        tenantId = createTestClub("Permission Test Club");
        createAdminUser(tenantId, "perm-admin@test.de", "AdminPass123!");
        adminToken = getAccessToken("perm-admin@test.de", "AdminPass123!");
    }

    @Test
    @DisplayName("Full staff lifecycle: invite → set-password → login → access endpoints")
    void fullStaffLifecycle_inviteToAccess() {
        // Step 1: Admin creates staff with RECORD_DISTRIBUTION permission
        CreateStaffRequest createRequest = new CreateStaffRequest(
                "staff1@test.de", "Staff One",
                Set.of(StaffPermission.RECORD_DISTRIBUTION), null);

        ResponseEntity<StaffResponse> createResponse = restClient().post()
                .uri("/api/v1/staff")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(createRequest)
                .retrieve()
                .toEntity(StaffResponse.class);

        assertThat(createResponse.getStatusCode().value()).isEqualTo(201);
        assertThat(createResponse.getBody()).isNotNull();
        assertThat(createResponse.getBody().email()).isEqualTo("staff1@test.de");

        // Step 2: Get the invite token from DB
        List<InviteToken> tokens = inviteTokenRepository.findAll();
        InviteToken inviteToken = tokens.stream()
                .filter(t -> t.getUsedAt() == null)
                .findFirst()
                .orElseThrow(() -> new AssertionError("No invite token found"));

        // Step 3: Staff sets password via invite token
        ResponseEntity<String> setPwResponse = restClient().post()
                .uri("/api/v1/auth/set-password")
                .contentType(MediaType.APPLICATION_JSON)
                .body(new SetPasswordRequest(inviteToken.getToken(), "StaffPass123!"))
                .retrieve()
                .toEntity(String.class);

        assertThat(setPwResponse.getStatusCode().value()).isEqualTo(200);

        // Step 4: Staff logs in
        LoginResponse staffLogin = login("staff1@test.de", "StaffPass123!");
        assertThat(staffLogin.role()).isEqualTo("STAFF");
        String staffToken = staffLogin.accessToken();

        // Step 5: Staff CAN access distributions endpoint (has RECORD_DISTRIBUTION)
        ResponseEntity<String> distResponse = restClient().get()
                .uri("/api/v1/distributions")
                .header("Authorization", "Bearer " + staffToken)
                .retrieve()
                .toEntity(String.class);

        assertThat(distResponse.getStatusCode().value()).isEqualTo(200);

        // Step 6: Staff CANNOT access stock endpoint (no VIEW_STOCK permission)
        ResponseEntity<String> stockResponse = restClient().get()
                .uri("/api/v1/stock/batches")
                .header("Authorization", "Bearer " + staffToken)
                .retrieve()
                .toEntity(String.class);

        assertThat(stockResponse.getStatusCode().value()).isEqualTo(403);
    }

    @Test
    @DisplayName("Staff without VIEW_MEMBER_LIST cannot list members")
    void staffWithoutViewMemberList_cannotListMembers() {
        CreateStaffRequest createRequest = new CreateStaffRequest(
                "staff2@test.de", "Staff Two",
                Set.of(StaffPermission.RECORD_DISTRIBUTION), null);

        restClient().post()
                .uri("/api/v1/staff")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(createRequest)
                .retrieve()
                .toEntity(StaffResponse.class);

        // Activate
        InviteToken inviteToken = inviteTokenRepository.findAll().stream()
                .filter(t -> t.getUsedAt() == null)
                .findFirst().orElseThrow();

        restClient().post()
                .uri("/api/v1/auth/set-password")
                .contentType(MediaType.APPLICATION_JSON)
                .body(new SetPasswordRequest(inviteToken.getToken(), "StaffPass123!"))
                .retrieve()
                .toEntity(String.class);

        String staffToken = getAccessToken("staff2@test.de", "StaffPass123!");

        // Try to list members — should be forbidden
        ResponseEntity<String> response = restClient().get()
                .uri("/api/v1/members")
                .header("Authorization", "Bearer " + staffToken)
                .retrieve()
                .toEntity(String.class);

        assertThat(response.getStatusCode().value()).isEqualTo(403);
    }

    @Test
    @DisplayName("Admin can update staff permissions")
    void admin_canUpdateStaffPermissions() {
        CreateStaffRequest createRequest = new CreateStaffRequest(
                "staff3@test.de", "Staff Three",
                Set.of(StaffPermission.RECORD_DISTRIBUTION), null);

        ResponseEntity<StaffResponse> createResp = restClient().post()
                .uri("/api/v1/staff")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(createRequest)
                .retrieve()
                .toEntity(StaffResponse.class);

        UUID staffId = createResp.getBody().id();

        // Update permissions to add VIEW_STOCK
        UpdateStaffRequest updateRequest = new UpdateStaffRequest(
                "Staff Three",
                Set.of(StaffPermission.RECORD_DISTRIBUTION, StaffPermission.VIEW_STOCK),
                null, true);

        ResponseEntity<StaffResponse> updateResp = restClient().put()
                .uri("/api/v1/staff/" + staffId)
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(updateRequest)
                .retrieve()
                .toEntity(StaffResponse.class);

        assertThat(updateResp.getStatusCode().value()).isEqualTo(200);
        assertThat(updateResp.getBody().permissions())
                .contains(StaffPermission.RECORD_DISTRIBUTION, StaffPermission.VIEW_STOCK);
    }
}
