<?php

namespace App\Services\ImportExport;

use App\Contracts\ImportExport\ModuleHandler;
use App\Exports\StyledReportExport;
use App\Models\User;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ExportService
{
    public function __construct(
        private readonly ModuleRegistry $registry,
        private readonly ImportExportLogService $logger,
    ) {}

    /**
     * @param  array<string, mixed>  $filters
     */
    public function export(User $user, string $module, string $format, array $filters, ?Request $request = null): BinaryFileResponse|Response|StreamedResponse
    {
        $handler = $this->registry->get($module);
        $this->authorize($user, $handler, 'view');

        $data = $handler->exportData($filters);
        $slug = Str::slug($module);
        $date = now()->format('Y-m-d');
        $filename = "{$slug}-{$date}";

        $meta = [
            'company' => config('southend.app_name'),
            'exported_at' => now()->format('d M Y H:i'),
            'filters' => $this->formatFilters($data['filters']),
            'totals' => isset($data['totals']) ? collect($data['totals'])->map(fn ($v, $k) => "{$k}: {$v}")->implode(' | ') : null,
        ];

        $this->logger->logExport(
            $user,
            $module,
            $format,
            "{$filename}.{$format}",
            $data['filters'],
            count($data['rows']),
            $request,
        );

        return match ($format) {
            'xlsx' => Excel::download(
                new StyledReportExport($data['title'], $data['headings'], $data['rows'], $meta),
                "{$filename}.xlsx",
            ),
            'csv' => $this->streamCsv($data, "{$filename}.csv"),
            'pdf' => Pdf::loadView('exports.report-table', [
                'title' => $data['title'],
                'headings' => $data['headings'],
                'rows' => $data['rows'],
                'meta' => $meta,
                'totals' => $data['totals'] ?? null,
                'landscape' => count($data['headings']) > 6,
            ])->setPaper('a4', count($data['headings']) > 6 ? 'landscape' : 'portrait')
                ->download("{$filename}.pdf"),
            default => abort(404, 'Unsupported export format.'),
        };
    }

    private function authorize(User $user, ModuleHandler $handler, string $action): void
    {
        if ($user->hasRole('Super Admin')) {
            return;
        }

        if (! $user->can("{$handler->permissionModule()}.{$action}")) {
            abort(403);
        }
    }

    /**
     * @param  array<string, mixed>  $filters
     */
    private function formatFilters(array $filters): ?string
    {
        $parts = collect($filters)
            ->filter(fn ($v) => $v !== null && $v !== '')
            ->map(fn ($v, $k) => "{$k}: {$v}")
            ->values();

        return $parts->isEmpty() ? null : $parts->implode(', ');
    }

    /**
     * @param  array{headings: list<string>, rows: list<list<mixed>>}  $data
     */
    private function streamCsv(array $data, string $filename): StreamedResponse
    {
        return response()->streamDownload(function () use ($data) {
            $handle = fopen('php://output', 'w');
            fputcsv($handle, $data['headings']);

            foreach ($data['rows'] as $row) {
                fputcsv($handle, $row);
            }

            fclose($handle);
        }, $filename, ['Content-Type' => 'text/csv']);
    }
}
