package de.cannamanage.api.controller;

import de.cannamanage.api.dto.finance.*;
import de.cannamanage.api.security.StaffPermissionChecker;
import de.cannamanage.domain.entity.*;
import de.cannamanage.domain.enums.PaymentStatus;
import de.cannamanage.domain.enums.StaffPermission;
import de.cannamanage.service.FinanceService;
import de.cannamanage.service.FinancialReportService;
import de.cannamanage.service.ReceiptPdfService;
import de.cannamanage.service.repository.ClubRepository;
import de.cannamanage.service.repository.MemberRepository;
import de.cannamanage.service.repository.PaymentRepository;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.*;

/**
 * REST controller for club treasury management.
 * Admin endpoints require MANAGE_FINANCES or VIEW_FINANCES permission.
 * Portal endpoints allow members to view their own payment history and balance.
 */
@RestController
@RequestMapping("/api/v1")
public class FinanceController {

    private final FinanceService financeService;
    private final StaffPermissionChecker permissionChecker;
    private final MemberRepository memberRepository;
    private final ReceiptPdfService receiptPdfService;
    private final FinancialReportService financialReportService;
    private final ClubRepository clubRepository;

    public FinanceController(FinanceService financeService,
                            StaffPermissionChecker permissionChecker,
                            MemberRepository memberRepository,
                            ReceiptPdfService receiptPdfService,
                            FinancialReportService financialReportService,
                            ClubRepository clubRepository) {
        this.financeService = financeService;
        this.permissionChecker = permissionChecker;
        this.memberRepository = memberRepository;
        this.receiptPdfService = receiptPdfService;
        this.financialReportService = financialReportService;
        this.clubRepository = clubRepository;
    }

    // === Fee Schedules ===

    @PostMapping("/finance/fee-schedules")
    public ResponseEntity<FeeSchedule> createFeeSchedule(@Valid @RequestBody CreateFeeScheduleRequest request,
                                                          @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        FeeSchedule schedule = financeService.createFeeSchedule(
                clubId, request.name(), request.amountCents(), request.interval(),
                request.isDefault() != null && request.isDefault()
        );

        return ResponseEntity.status(HttpStatus.CREATED).body(schedule);
    }

    @GetMapping("/finance/fee-schedules")
    public ResponseEntity<List<FeeSchedule>> listFeeSchedules(@AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();
        return ResponseEntity.ok(financeService.getActiveFeeSchedules(clubId));
    }

    @PutMapping("/finance/fee-schedules/{id}")
    public ResponseEntity<FeeSchedule> updateFeeSchedule(@PathVariable UUID id,
                                                          @RequestBody UpdateFeeScheduleRequest request,
                                                          @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        FeeSchedule updated = financeService.updateFeeSchedule(
                id, request.name(), request.amountCents(), request.interval(), request.isDefault()
        );
        return ResponseEntity.ok(updated);
    }

    @PostMapping("/finance/fee-schedules/{id}/deactivate")
    public ResponseEntity<Void> deactivateFeeSchedule(@PathVariable UUID id,
                                                       @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        financeService.deactivateFeeSchedule(id);
        return ResponseEntity.noContent().build();
    }

    // === Fee Assignment ===

    @PostMapping("/finance/members/{memberId}/assign-fee")
    public ResponseEntity<MemberFeeAssignment> assignFeeSchedule(@PathVariable UUID memberId,
                                                                   @Valid @RequestBody AssignFeeRequest request,
                                                                   @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        MemberFeeAssignment assignment = financeService.assignFeeSchedule(
                memberId, clubId, request.feeScheduleId(), request.validFrom()
        );

        return ResponseEntity.status(HttpStatus.CREATED).body(assignment);
    }

    // === Payments ===

    @PostMapping("/finance/payments")
    public ResponseEntity<Payment> recordPayment(@Valid @RequestBody RecordPaymentRequest request,
                                                  @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();
        UUID userId = UUID.fromString(principal.getUsername());

        Payment payment = financeService.recordPayment(
                clubId, request.memberId(), request.amountCents(), request.paymentMethod(),
                request.periodFrom(), request.periodTo(), request.reference(), request.notes(), userId
        );

        return ResponseEntity.status(HttpStatus.CREATED).body(payment);
    }

    @GetMapping("/finance/payments")
    public ResponseEntity<Page<Payment>> listPayments(
            @RequestParam(required = false) UUID memberId,
            @RequestParam(required = false) PaymentStatus status,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();
        var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));

        Page<Payment> result;
        if (memberId != null) {
            result = financeService.getPaymentsByMember(clubId, memberId, pageable);
        } else if (status != null) {
            result = financeService.getPaymentsByStatus(clubId, status, pageable);
        } else {
            result = financeService.getPayments(clubId, pageable);
        }

        return ResponseEntity.ok(result);
    }

    @PostMapping("/finance/payments/{id}/void")
    public ResponseEntity<Payment> voidPayment(@PathVariable UUID id,
                                                @Valid @RequestBody VoidPaymentRequest request,
                                                @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        UUID userId = UUID.fromString(principal.getUsername());

        Payment voided = financeService.voidPayment(id, userId, request.reason());
        return ResponseEntity.ok(voided);
    }

    // === Expenses ===

    @PostMapping("/finance/expenses")
    public ResponseEntity<LedgerEntry> recordExpense(@Valid @RequestBody RecordExpenseRequest request,
                                                      @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.MANAGE_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();
        UUID userId = UUID.fromString(principal.getUsername());

        LedgerEntry entry = financeService.recordExpense(
                clubId, request.category(), request.amountCents(),
                request.description(), request.reference(), userId, request.transactionDate()
        );

        return ResponseEntity.status(HttpStatus.CREATED).body(entry);
    }

    // === Ledger / Kassenbuch ===

    @GetMapping("/finance/ledger")
    public ResponseEntity<Page<LedgerEntry>> getLedger(
            @RequestParam LocalDate from,
            @RequestParam LocalDate to,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "50") int size,
            @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();
        var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "transactionDate"));

        return ResponseEntity.ok(financeService.getLedgerEntries(clubId, from, to, pageable));
    }

    // === Financial Summary ===

    @GetMapping("/finance/summary")
    public ResponseEntity<Map<String, Object>> getFinancialSummary(
            @RequestParam LocalDate from,
            @RequestParam LocalDate to,
            @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        return ResponseEntity.ok(financeService.getFinancialSummary(clubId, from, to));
    }

    // === Outstanding ===

    @GetMapping("/finance/outstanding")
    public ResponseEntity<List<Map<String, Object>>> getOutstandingMembers(
            @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        return ResponseEntity.ok(financeService.getOutstandingMembers(clubId));
    }

    // === Member Balance (Admin) ===

    @GetMapping("/finance/members/{memberId}/balance")
    public ResponseEntity<Map<String, Object>> getMemberBalance(@PathVariable UUID memberId,
                                                                 @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        return ResponseEntity.ok(financeService.getMemberBalance(clubId, memberId));
    }

    // === Portal Endpoints (member self-service) ===

    @GetMapping("/portal/finance/payments")
    public ResponseEntity<Page<Payment>> getMyPayments(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @AuthenticationPrincipal UserDetails principal) {
        UUID userId = UUID.fromString(principal.getUsername());
        UUID clubId = TenantContext.getCurrentTenant();
        UUID memberId = getMemberIdForUser(userId, clubId);

        var pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        return ResponseEntity.ok(financeService.getPaymentsByMember(clubId, memberId, pageable));
    }

    @GetMapping("/portal/finance/balance")
    public ResponseEntity<Map<String, Object>> getMyBalance(@AuthenticationPrincipal UserDetails principal) {
        UUID userId = UUID.fromString(principal.getUsername());
        UUID clubId = TenantContext.getCurrentTenant();
        UUID memberId = getMemberIdForUser(userId, clubId);

        return ResponseEntity.ok(financeService.getMemberBalance(clubId, memberId));
    }

    // === Receipt PDF Download ===

    @GetMapping("/finance/payments/{id}/receipt")
    public ResponseEntity<byte[]> downloadReceipt(@PathVariable UUID id,
                                                   @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        Payment payment = financeService.getPaymentById(id)
                .orElseThrow(() -> new NoSuchElementException("Payment not found: " + id));
        Member member = memberRepository.findById(payment.getMemberId())
                .orElseThrow(() -> new NoSuchElementException("Member not found"));
        Club club = clubRepository.findById(clubId)
                .orElseThrow(() -> new NoSuchElementException("Club not found"));

        byte[] pdf = receiptPdfService.generateReceipt(payment, member, club);
        String filename = "Quittung-" + (payment.getReceiptNumber() != null
                ? payment.getReceiptNumber() : id.toString().substring(0, 8)) + ".pdf";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                .contentType(MediaType.APPLICATION_PDF)
                .contentLength(pdf.length)
                .body(pdf);
    }

    // === Annual Report PDF ===

    @GetMapping("/finance/reports/annual")
    public ResponseEntity<byte[]> downloadAnnualReport(@RequestParam int year,
                                                        @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        Club club = clubRepository.findById(clubId)
                .orElseThrow(() -> new NoSuchElementException("Club not found"));

        FinancialReportService.AnnualReportData reportData = financeService.buildAnnualReportData(clubId, year);
        byte[] pdf = financialReportService.generateAnnualReport(reportData, club);
        String filename = "Jahresabschluss-" + year + "-" + club.getName().replaceAll("\\s+", "_") + ".pdf";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                .contentType(MediaType.APPLICATION_PDF)
                .contentLength(pdf.length)
                .body(pdf);
    }

    // === Kassenbuch CSV Export ===

    @GetMapping("/finance/ledger/export")
    public ResponseEntity<byte[]> exportLedgerCsv(@RequestParam LocalDate from,
                                                    @RequestParam LocalDate to,
                                                    @AuthenticationPrincipal UserDetails principal) {
        permissionChecker.requirePermission(principal, StaffPermission.VIEW_FINANCES);
        UUID clubId = TenantContext.getCurrentTenant();

        byte[] csv = financeService.exportLedgerCsv(clubId, from, to);
        String filename = "Kassenbuch-" + from + "-" + to + ".csv";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                .contentType(MediaType.parseMediaType("text/csv; charset=ISO-8859-1"))
                .contentLength(csv.length)
                .body(csv);
    }

    // === Portal: Receipt download (own payments only) ===

    @GetMapping("/portal/finance/payments/{id}/receipt")
    public ResponseEntity<byte[]> downloadMyReceipt(@PathVariable UUID id,
                                                     @AuthenticationPrincipal UserDetails principal) {
        UUID userId = UUID.fromString(principal.getUsername());
        UUID clubId = TenantContext.getCurrentTenant();
        UUID memberId = getMemberIdForUser(userId, clubId);

        Payment payment = financeService.getPaymentById(id)
                .orElseThrow(() -> new NoSuchElementException("Payment not found: " + id));

        // Verify payment belongs to the requesting member
        if (!payment.getMemberId().equals(memberId)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        Member member = memberRepository.findById(memberId)
                .orElseThrow(() -> new NoSuchElementException("Member not found"));
        Club club = clubRepository.findById(clubId)
                .orElseThrow(() -> new NoSuchElementException("Club not found"));

        byte[] pdf = receiptPdfService.generateReceipt(payment, member, club);
        String filename = "Quittung-" + (payment.getReceiptNumber() != null
                ? payment.getReceiptNumber() : id.toString().substring(0, 8)) + ".pdf";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
                .contentType(MediaType.APPLICATION_PDF)
                .contentLength(pdf.length)
                .body(pdf);
    }

    private UUID getMemberIdForUser(UUID userId, UUID clubId) {
        return memberRepository.findByUserId(userId)
                .map(Member::getId)
                .orElseThrow(() -> new NoSuchElementException("Member not found for user: " + userId));
    }
}
