<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Controller;

use OCA\Mitgliederverwaltung\Service\EncryptedExportService;
use OCA\Mitgliederverwaltung\Service\InventoryReportService;
use OCA\Mitgliederverwaltung\Service\PdfService;
use OCA\Mitgliederverwaltung\Service\PermissionService;
use OCA\Mitgliederverwaltung\Service\ReportService;
use OCA\Mitgliederverwaltung\Service\ValidationException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
use Psr\Log\LoggerInterface;

/**
 * REST API controller for report generation and preview.
 *
 * Supports:
 * - Preview: returns structured JSON data for frontend table display
 * - PDF download: generates and returns a PDF file
 * - Encrypted PDF: wraps the PDF in a password-protected ZIP
 *
 * Part of Issue #46.
 */
class ReportController extends ApiController {

    private ReportService $reportService;
    private InventoryReportService $inventoryReportService;
    private PdfService $pdfService;
    private EncryptedExportService $encryptedService;
    private PermissionService $permissionService;
    private IUserSession $userSession;
    private LoggerInterface $logger;

    public function __construct(
        string $appName,
        IRequest $request,
        ReportService $reportService,
        InventoryReportService $inventoryReportService,
        PdfService $pdfService,
        EncryptedExportService $encryptedService,
        PermissionService $permissionService,
        IUserSession $userSession,
        LoggerInterface $logger
    ) {
        parent::__construct($appName, $request);
        $this->reportService = $reportService;
        $this->inventoryReportService = $inventoryReportService;
        $this->pdfService = $pdfService;
        $this->encryptedService = $encryptedService;
        $this->permissionService = $permissionService;
        $this->userSession = $userSession;
        $this->logger = $logger;
    }

    /**
     * List available report types.
     *
     * GET /api/v1/reports
     */
    #[NoCSRFRequired]
    public function index(): JSONResponse {
        return new JSONResponse([
            'reports' => [
                ['id' => 'mitgliederliste',    'name' => 'Mitgliederliste',     'params' => ['status']],
                ['id' => 'beitragsliste',      'name' => 'Beitragsliste',       'params' => ['year']],
                ['id' => 'stufenliste',        'name' => 'Stufenliste',         'params' => []],
                ['id' => 'allergieliste',      'name' => 'Allergieliste',       'params' => []],
                ['id' => 'geburtstagsliste',   'name' => 'Geburtstagsliste',   'params' => ['months']],
                ['id' => 'kontaktliste',       'name' => 'Kontaktliste',       'params' => ['stufeId']],
                ['id' => 'bankverbindungen',   'name' => 'Bankverbindungen',   'params' => [], 'sensitive' => true],
                ['id' => 'familienliste',      'name' => 'Familienliste',      'params' => []],
                ['id' => 'lagerhistorie',      'name' => 'Lagerhistorie',     'params' => ['memberId']],
                ['id' => 'verletzungsprotokoll', 'name' => 'Verletzungsprotokoll', 'params' => ['dateFrom', 'dateTo', 'memberId']],
                ['id' => 'inventur-verkaeufe',  'name' => 'Inventur-Verkaeufe',  'params' => ['dateFrom', 'dateTo']],
                ['id' => 'materialzustand',     'name' => 'Materialzustand',     'params' => []],
            ],
        ]);
    }

    /**
     * Preview report data as JSON (for frontend table display).
     *
     * GET /api/v1/reports/{type}/preview?...params
     */
    #[NoCSRFRequired]
    public function preview(string $type): JSONResponse {
        try {
            $data = $this->generateReportData($type);

            if ($data === null) {
                return new JSONResponse(
                    ['error' => "Unbekannter Berichtstyp: {$type}"],
                    Http::STATUS_BAD_REQUEST
                );
            }

            return new JSONResponse($data);
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_FORBIDDEN
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to generate report preview', [
                'type' => $type,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Berichtsvorschau fehlgeschlagen'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Download report as PDF.
     *
     * GET /api/v1/reports/{type}/pdf?...params
     */
    #[NoCSRFRequired]
    public function pdf(string $type): DataDownloadResponse|JSONResponse {
        try {
            $data = $this->generateReportData($type);

            if ($data === null) {
                return new JSONResponse(
                    ['error' => "Unbekannter Berichtstyp: {$type}"],
                    Http::STATUS_BAD_REQUEST
                );
            }

            $result = $this->pdfService->generateReport($data);

            return new DataDownloadResponse(
                $result['content'],
                $result['filename'],
                'application/pdf'
            );
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_FORBIDDEN
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to generate PDF report', [
                'type' => $type,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'PDF-Erzeugung fehlgeschlagen'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Download report as encrypted PDF (password-protected ZIP).
     *
     * POST /api/v1/reports/{type}/encrypted
     * Body: { "password": "...", ...params }
     */
    public function encrypted(string $type): DataDownloadResponse|JSONResponse {
        try {
            $password = $this->request->getParam('password');
            if ($password === null || $password === '') {
                return new JSONResponse(
                    ['error' => 'Passwort ist erforderlich'],
                    Http::STATUS_BAD_REQUEST
                );
            }

            $data = $this->generateReportData($type);

            if ($data === null) {
                return new JSONResponse(
                    ['error' => "Unbekannter Berichtstyp: {$type}"],
                    Http::STATUS_BAD_REQUEST
                );
            }

            $pdfResult = $this->pdfService->generateReport($data);

            $zipResult = $this->encryptedService->createEncryptedExport(
                $pdfResult['content'],
                $pdfResult['filename'],
                $password,
                $data['title']
            );

            return new DataDownloadResponse(
                $zipResult['content'],
                $zipResult['filename'],
                'application/zip'
            );
        } catch (ValidationException $e) {
            return new JSONResponse(
                ['error' => $e->getMessage()],
                Http::STATUS_FORBIDDEN
            );
        } catch (\Exception $e) {
            $this->logger->error('Failed to generate encrypted report', [
                'type' => $type,
                'exception' => $e,
                'app' => 'mitgliederverwaltung',
            ]);
            return new JSONResponse(
                ['error' => 'Verschluesselter Bericht fehlgeschlagen'],
                Http::STATUS_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Route to the appropriate report generator based on type.
     *
     * @return array{title: string, headers: string[], rows: array[]}|null
     */
    private function generateReportData(string $type): ?array {
        return match ($type) {
            'mitgliederliste' => $this->reportService->generateMitgliederliste(
                $this->request->getParam('status')
            ),
            'beitragsliste' => $this->reportService->generateBeitragsliste(
                (int)($this->request->getParam('year') ?? (new \DateTime())->format('Y'))
            ),
            'stufenliste' => $this->reportService->generateStufenliste(),
            'allergieliste' => $this->reportService->generateAllergieliste(),
            'geburtstagsliste' => $this->reportService->generateGeburtstagsliste(
                (int)($this->request->getParam('months') ?? 3)
            ),
            'kontaktliste' => $this->reportService->generateKontaktliste(
                $this->request->getParam('stufeId') !== null
                    ? (int)$this->request->getParam('stufeId')
                    : null
            ),
            'bankverbindungen' => $this->generateBankverbindungenWithPermCheck(),
            'familienliste' => $this->reportService->generateFamilienliste(),
            'lagerhistorie' => $this->reportService->generateLagerhistorie(
                $this->request->getParam('memberId') !== null
                    ? (int)$this->request->getParam('memberId')
                    : null
            ),
            'verletzungsprotokoll' => $this->reportService->generateVerletzungsprotokoll(
                $this->request->getParam('dateFrom') ?: null,
                $this->request->getParam('dateTo') ?: null,
                $this->request->getParam('memberId') !== null
                    ? (int)$this->request->getParam('memberId')
                    : null
            ),
            'inventur-verkaeufe' => $this->inventoryReportService->generateSalesReport(
                $this->request->getParam('dateFrom') ?: null,
                $this->request->getParam('dateTo') ?: null
            ),
            'materialzustand' => $this->inventoryReportService->generateConditionReport(),
            default => null,
        };
    }

    /**
     * Generate Bankverbindungen report with permission check.
     *
     * @return array{title: string, headers: string[], rows: array[]}
     * @throws ValidationException
     */
    private function generateBankverbindungenWithPermCheck(): array {
        $userId = $this->getCurrentUserId();

        if (!$this->permissionService->canSeeBanking($userId)) {
            throw new ValidationException(
                'Keine Berechtigung: Bankdaten-Bericht erfordert Bankdaten-Sichtbarkeit.'
            );
        }

        return $this->reportService->generateBankverbindungen();
    }

    private function getCurrentUserId(): string {
        $user = $this->userSession->getUser();
        return $user ? $user->getUID() : 'system';
    }
}
