package de.cannamanage.api.controller;

import de.cannamanage.api.dto.report.MemberListResponse;
import de.cannamanage.api.dto.report.MonthlyReportResponse;
import de.cannamanage.api.dto.report.RecallReportResponse;
import de.cannamanage.domain.entity.Club;
import de.cannamanage.domain.entity.TenantContext;
import de.cannamanage.domain.enums.MemberStatus;
import de.cannamanage.service.CsvReportGenerator;
import de.cannamanage.service.PdfReportGenerator;
import de.cannamanage.service.ReportService;
import de.cannamanage.service.model.report.MemberListReport;
import de.cannamanage.service.model.report.MonthlyReport;
import de.cannamanage.service.model.report.RecallReport;
import de.cannamanage.service.repository.ClubRepository;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.time.YearMonth;
import java.util.UUID;

/**
 * REST controller for compliance and operational reports.
 * Supports JSON, PDF, and CSV output formats.
 */
@RestController
@RequestMapping("/api/v1/reports")
public class ReportController {

    private final ReportService reportService;
    private final PdfReportGenerator pdfGenerator;
    private final CsvReportGenerator csvGenerator;
    private final ClubRepository clubRepository;

    public ReportController(ReportService reportService,
                            PdfReportGenerator pdfGenerator,
                            CsvReportGenerator csvGenerator,
                            ClubRepository clubRepository) {
        this.reportService = reportService;
        this.pdfGenerator = pdfGenerator;
        this.csvGenerator = csvGenerator;
        this.clubRepository = clubRepository;
    }

    /**
     * Monthly distribution report.
     * GET /api/v1/reports/monthly?month=2026-03&format=json|pdf|csv
     */
    @GetMapping("/monthly")
    @PreAuthorize("hasRole('ADMIN') or @staffPermissions.has(#root, T(de.cannamanage.domain.enums.StaffPermission).VIEW_COMPLIANCE_REPORT)")
    public ResponseEntity<?> monthlyReport(
            @RequestParam String month,
            @RequestParam(defaultValue = "json") String format) {

        UUID tenantId = TenantContext.getCurrentTenant();
        YearMonth ym = YearMonth.parse(month);
        MonthlyReport report = reportService.generateMonthlyReport(tenantId, ym);

        return switch (format.toLowerCase()) {
            case "pdf" -> {
                Club club = getClub(tenantId);
                byte[] pdf = pdfGenerator.renderMonthlyReport(report, club);
                yield ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"monatsbericht-" + month + ".pdf\"")
                        .contentType(MediaType.APPLICATION_PDF)
                        .body(pdf);
            }
            case "csv" -> {
                byte[] csv = csvGenerator.renderMonthlyReport(report);
                yield ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"monatsbericht-" + month + ".csv\"")
                        .contentType(new MediaType("text", "csv", java.nio.charset.StandardCharsets.UTF_8))
                        .body(csv);
            }
            default -> ResponseEntity.ok(toMonthlyResponse(report));
        };
    }

    /**
     * Member list report.
     * GET /api/v1/reports/members?format=json|pdf|csv&status=ACTIVE
     */
    @GetMapping("/members")
    @PreAuthorize("hasRole('ADMIN') or @staffPermissions.has(#root, T(de.cannamanage.domain.enums.StaffPermission).VIEW_COMPLIANCE_REPORT)")
    public ResponseEntity<?> memberListReport(
            @RequestParam(defaultValue = "json") String format,
            @RequestParam(required = false) MemberStatus status) {

        UUID tenantId = TenantContext.getCurrentTenant();
        MemberListReport report = reportService.generateMemberListReport(tenantId, status);

        return switch (format.toLowerCase()) {
            case "pdf" -> {
                Club club = getClub(tenantId);
                byte[] pdf = pdfGenerator.renderMemberList(report, club);
                yield ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"mitgliederliste.pdf\"")
                        .contentType(MediaType.APPLICATION_PDF)
                        .body(pdf);
            }
            case "csv" -> {
                byte[] csv = csvGenerator.renderMemberList(report);
                yield ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"mitgliederliste.csv\"")
                        .contentType(new MediaType("text", "csv", java.nio.charset.StandardCharsets.UTF_8))
                        .body(csv);
            }
            default -> ResponseEntity.ok(toMemberListResponse(report));
        };
    }

    /**
     * Recall/batch trace report.
     * GET /api/v1/reports/recall/{batchId}?format=json|pdf
     */
    @GetMapping("/recall/{batchId}")
    @PreAuthorize("hasRole('ADMIN') or @staffPermissions.has(#root, T(de.cannamanage.domain.enums.StaffPermission).VIEW_COMPLIANCE_REPORT)")
    public ResponseEntity<?> recallReport(
            @PathVariable UUID batchId,
            @RequestParam(defaultValue = "json") String format) {

        UUID tenantId = TenantContext.getCurrentTenant();
        RecallReport report = reportService.generateRecallReport(tenantId, batchId);

        return switch (format.toLowerCase()) {
            case "pdf" -> {
                Club club = getClub(tenantId);
                byte[] pdf = pdfGenerator.renderRecallReport(report, club);
                yield ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=\"rueckruf-" + batchId + ".pdf\"")
                        .contentType(MediaType.APPLICATION_PDF)
                        .body(pdf);
            }
            default -> ResponseEntity.ok(toRecallResponse(report));
        };
    }

    // --- Mapping helpers ---

    private Club getClub(UUID tenantId) {
        return clubRepository.findByTenantId(tenantId)
                .orElseThrow(() -> new IllegalStateException("Club not found for tenant " + tenantId));
    }

    private MonthlyReportResponse toMonthlyResponse(MonthlyReport r) {
        return new MonthlyReportResponse(
                r.getMonth().toString(),
                r.getTotalDistributions(),
                r.getTotalGrams(),
                r.getUniqueMembers(),
                r.getAveragePerMember(),
                r.getTopStrains().stream()
                        .map(s -> new MonthlyReportResponse.StrainSummaryDto(
                                s.getName(), s.getTotalGrams(), s.getDistributionCount()))
                        .toList(),
                r.getDailyBreakdown().stream()
                        .map(d -> new MonthlyReportResponse.DailyEntryDto(
                                d.getDate(), d.getGrams(), d.getDistributions()))
                        .toList()
        );
    }

    private MemberListResponse toMemberListResponse(MemberListReport r) {
        return new MemberListResponse(
                r.getGeneratedAt(),
                r.getMembers().stream()
                        .map(m -> new MemberListResponse.MemberEntryDto(
                                m.getId(), m.getFirstName(), m.getLastName(),
                                m.getMembershipNumber(),
                                m.getStatus() != null ? m.getStatus().name() : null,
                                m.getJoinDate(), m.getTotalDistributions(),
                                m.getLastDistributionDate()))
                        .toList()
        );
    }

    private RecallReportResponse toRecallResponse(RecallReport r) {
        return new RecallReportResponse(
                r.getBatchId(),
                r.getStrainName(),
                r.getBatchNumber(),
                r.getReceivedDate(),
                r.getTotalGramsDistributed(),
                r.getAffectedMembers().stream()
                        .map(am -> new RecallReportResponse.AffectedMemberDto(
                                am.getMemberId(), am.getFirstName(), am.getLastName(),
                                am.getDistributionDate(), am.getGrams()))
                        .toList()
        );
    }
}
