<?php

declare(strict_types=1);

namespace Maestro\Core\Http\Controllers\Api\DataTables;

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Maestro\Core\DataTables\DataTableRegistry;
use Maestro\Core\Http\Controllers\Controller;
use Maestro\Core\Models\DataTableExport;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

use function activity;

final class DataTableExportController extends Controller
{
    public function __construct(
        private readonly DataTableRegistry $registry,
    ) {}

    public function clearReady(Request $request): JsonResponse
    {
        $userId = $request->user()?->id;
        if (! $userId) {
            return response()->json(['message' => 'Unauthenticated.'], 401);
        }

        $exports = DataTableExport::query()
            ->where('user_id', $userId)
            ->where('status', DataTableExport::STATUS_READY)
            ->whereNotNull('file_path')
            ->get();

        $disk = Storage::disk('exports');
        $cleared = 0;
        $filesDeleted = 0;

        foreach ($exports as $export) {
            $cleared++;
            if ($export->file_path && $disk->exists($export->file_path)) {
                $disk->delete($export->file_path);
                $filesDeleted++;
            }

            $export->update([
                'status' => DataTableExport::STATUS_COMPLETED,
            ]);
        }

        return response()->json([
            'cleared' => $cleared,
            'filesDeleted' => $filesDeleted,
        ]);
    }

    public function index(Request $request): JsonResponse
    {
        $userId = $request->user()?->id;
        if (! $userId) {
            return response()->json(['data' => []], 200);
        }

        $exports = DataTableExport::query()
            ->where('user_id', $userId)
            ->where('status', DataTableExport::STATUS_READY)
            ->whereNotNull('file_path')
            ->where('download_expires_at', '>', now())
            ->orderByDesc('created_at')
            ->limit(20)
            ->get();

        $data = [];
        foreach ($exports as $export) {
            try {
                $resource = $this->registry->resolve($export->resource_key);
                $resource->authorize($request);
            } catch (AuthorizationException) {
                continue;
            }

            $item = [
                'id' => $export->id,
                'resourceKey' => $export->resource_key,
                'status' => $export->status,
                'createdAt' => $export->created_at?->toIso8601String(),
            ];

            $resourceLabel = trans('maestro::datatable.resources.'.$export->resource_key);
            if ($resourceLabel === 'maestro::datatable.resources.'.$export->resource_key) {
                $resourceLabel = $export->resource_key;
            }
            $item['resourceLabel'] = $resourceLabel;

            if ($export->isDownloadAvailable()) {
                $item['downloadUrl'] = url('/api/datatables/exports/'.$export->id.'/download');
            }

            if ($export->isFailed() && $export->error_message) {
                $item['errorMessage'] = $export->error_message;
            }

            $data[] = $item;
        }

        return response()->json(['data' => $data]);
    }

    public function show(Request $request, string $id): JsonResponse
    {
        $export = DataTableExport::query()->find($id);
        if (! $export) {
            return response()->json(['message' => 'Export not found.'], 404);
        }

        if ((string) $export->user_id !== (string) $request->user()?->id) {
            return response()->json(['message' => 'Unauthorized.'], 403);
        }

        $resource = $this->registry->resolve($export->resource_key);
        try {
            $resource->authorize($request);
        } catch (AuthorizationException) {
            return response()->json(['message' => 'You no longer have permission to access this export.'], 403);
        }

        $resourceLabel = trans('maestro::datatable.resources.'.$export->resource_key);
        if ($resourceLabel === 'maestro::datatable.resources.'.$export->resource_key) {
            $resourceLabel = $export->resource_key;
        }

        $payload = [
            'id' => $export->id,
            'status' => $export->status,
            'resourceKey' => $export->resource_key,
            'resourceLabel' => $resourceLabel,
        ];

        if ($export->isDownloadAvailable()) {
            $payload['downloadUrl'] = url('/api/datatables/exports/'.$export->id.'/download');
        }

        if ($export->isFailed() && $export->error_message) {
            $payload['errorMessage'] = $export->error_message;
        }

        return response()->json($payload);
    }

    public function download(Request $request, string $id): StreamedResponse|JsonResponse|BinaryFileResponse
    {
        $export = DataTableExport::query()->find($id);
        if (! $export) {
            return response()->json(['message' => 'Export not found.'], 404);
        }

        if ((string) $export->user_id !== (string) $request->user()?->id) {
            return response()->json(['message' => 'Unauthorized.'], 403);
        }

        $resource = $this->registry->resolve($export->resource_key);
        try {
            $resource->authorize($request);
        } catch (AuthorizationException) {
            return response()->json(['message' => 'You do not have permission to download this export.'], 403);
        }

        if (! $export->isDownloadAvailable()) {
            return response()->json([
                'message' => $export->status === DataTableExport::STATUS_READY
                    ? 'Download link has expired.'
                    : 'Export is no longer available for download.',
            ], 410);
        }

        $path = $export->file_path;
        $disk = Storage::disk('exports');
        if (! $path || ! $disk->exists($path)) {
            return response()->json(['message' => 'Export file no longer available.'], 410);
        }

        $filename = basename($path);
        $absolutePath = $disk->path($path);

        app()->terminating(function () use ($export, $path, $disk, $request): void {
            if ($disk->exists($path)) {
                $disk->delete($path);
            }
            $export->update([
                'status' => DataTableExport::STATUS_COMPLETED,
            ]);

            if ($request->user()) {
                activity('data_export')
                    ->causedBy($request->user())
                    ->event('export')
                    ->withProperties([
                        'resource_key' => $export->resource_key,
                        'export_id' => $export->id,
                        'status' => DataTableExport::STATUS_COMPLETED,
                    ])
                    ->log('Data export to CSV downloaded for: '.$export->resource_key);
            }
        });

        return response()->download($absolutePath, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
            'Cache-Control' => 'no-cache, no-store, must-revalidate',
            'Pragma' => 'no-cache',
            'Expires' => '0',
        ]);
    }
}
