package de.cannamanage.api.integration;

import de.cannamanage.api.dto.distribution.CreateDistributionRequest;
import de.cannamanage.api.dto.member.MemberResponse;
import de.cannamanage.api.dto.stock.BatchResponse;
import de.cannamanage.domain.entity.Strain;
import de.cannamanage.domain.entity.TenantContext;
import de.cannamanage.service.repository.StrainRepository;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

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

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

/**
 * Integration test: Report generation E2E.
 * Verifies JSON, PDF, and CSV output for monthly reports and recall reports.
 */
class ReportIntegrationTest extends AbstractIntegrationTest {

    @Autowired
    private StrainRepository strainRepository;

    private UUID tenantId;
    private String adminToken;

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

    private Strain createTestStrain(String name) {
        Strain strain = new Strain();
        strain.setTenantId(tenantId);
        strain.setName(name);
        strain.setThcPercentage(new BigDecimal("18.5"));
        strain.setCbdPercentage(new BigDecimal("0.5"));
        TenantContext.setCurrentTenant(tenantId);
        strain = strainRepository.save(strain);
        TenantContext.clear();
        return strain;
    }

    @Test
    @DisplayName("Monthly report JSON — returns totals and distribution data")
    void monthlyReportJson_returnsTotals() {
        MemberResponse member = createTestMember(adminToken, "Report", "Member",
                "report-member@test.de", LocalDate.of(1990, 3, 15));

        Strain strain = createTestStrain("Test Strain");
        BatchResponse batch = createTestBatch(adminToken, strain.getId(),
                new BigDecimal("500.0"), "BATCH-R-001");

        // Create a distribution
        restClient().post()
                .uri("/api/v1/distributions")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new CreateDistributionRequest(
                        member.id(), batch.id(), new BigDecimal("5.0"), "Report test"))
                .retrieve()
                .toEntity(String.class);

        // Get monthly report as JSON
        String currentMonth = YearMonth.now().toString();
        ResponseEntity<String> response = restClient().get()
                .uri("/api/v1/reports/monthly?month=" + currentMonth + "&format=json")
                .header("Authorization", "Bearer " + adminToken)
                .retrieve()
                .toEntity(String.class);

        assertThat(response.getStatusCode().value()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody()).contains("totalDistributions");
        assertThat(response.getBody()).contains("totalGrams");
    }

    @Test
    @DisplayName("Monthly report PDF — returns valid PDF bytes")
    void monthlyReportPdf_returnsValidPdf() {
        createTestMember(adminToken, "PDF", "Member",
                "pdf-member@test.de", LocalDate.of(1990, 3, 15));

        String currentMonth = YearMonth.now().toString();
        ResponseEntity<byte[]> response = restClient().get()
                .uri("/api/v1/reports/monthly?month=" + currentMonth + "&format=pdf")
                .header("Authorization", "Bearer " + adminToken)
                .retrieve()
                .toEntity(byte[].class);

        assertThat(response.getStatusCode().value()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().length).isGreaterThan(0);
        // PDF starts with %PDF
        assertThat(new String(response.getBody(), 0, 4)).isEqualTo("%PDF");
    }

    @Test
    @DisplayName("Monthly report CSV — returns UTF-8 BOM + headers")
    void monthlyReportCsv_returnsValidCsv() {
        createTestMember(adminToken, "CSV", "Member",
                "csv-member@test.de", LocalDate.of(1990, 3, 15));

        String currentMonth = YearMonth.now().toString();
        ResponseEntity<byte[]> response = restClient().get()
                .uri("/api/v1/reports/monthly?month=" + currentMonth + "&format=csv")
                .header("Authorization", "Bearer " + adminToken)
                .retrieve()
                .toEntity(byte[].class);

        assertThat(response.getStatusCode().value()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().length).isGreaterThan(0);

        // Check UTF-8 BOM (0xEF 0xBB 0xBF)
        assertThat(response.getBody()[0]).isEqualTo((byte) 0xEF);
        assertThat(response.getBody()[1]).isEqualTo((byte) 0xBB);
        assertThat(response.getBody()[2]).isEqualTo((byte) 0xBF);

        // Verify CSV has separator content
        String csvContent = new String(response.getBody(), java.nio.charset.StandardCharsets.UTF_8);
        assertThat(csvContent).contains(";"); // German CSV uses semicolons
    }

    @Test
    @DisplayName("Recall report — returns affected members for a batch")
    void recallReport_returnsAffectedMembers() {
        MemberResponse member1 = createTestMember(adminToken, "Recall", "One",
                "recall1@test.de", LocalDate.of(1990, 3, 15));
        MemberResponse member2 = createTestMember(adminToken, "Recall", "Two",
                "recall2@test.de", LocalDate.of(1988, 7, 20));

        Strain strain = createTestStrain("Recall Strain");
        BatchResponse batch = createTestBatch(adminToken, strain.getId(),
                new BigDecimal("1000.0"), "BATCH-RECALL-001");

        // Both members get distributions from this batch
        restClient().post()
                .uri("/api/v1/distributions")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new CreateDistributionRequest(
                        member1.id(), batch.id(), new BigDecimal("3.0"), "recall test 1"))
                .retrieve()
                .toEntity(String.class);

        restClient().post()
                .uri("/api/v1/distributions")
                .header("Authorization", "Bearer " + adminToken)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new CreateDistributionRequest(
                        member2.id(), batch.id(), new BigDecimal("4.0"), "recall test 2"))
                .retrieve()
                .toEntity(String.class);

        // Generate recall report for the batch
        ResponseEntity<String> response = restClient().get()
                .uri("/api/v1/reports/recall?batchId=" + batch.id() + "&format=json")
                .header("Authorization", "Bearer " + adminToken)
                .retrieve()
                .toEntity(String.class);

        assertThat(response.getStatusCode().value()).isEqualTo(200);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody()).contains("affectedMembers");
        assertThat(response.getBody()).contains("Recall");
    }

    @Test
    @DisplayName("Non-admin cannot access reports")
    void nonAdmin_cannotAccessReports() {
        ResponseEntity<String> response = restClient().get()
                .uri("/api/v1/reports/monthly?month=2026-01&format=json")
                .retrieve()
                .toEntity(String.class);

        assertThat(response.getStatusCode().value()).isIn(401, 403);
    }
}
