<?php

namespace Modules\Activity\Http\Controllers;

use App\Http\Controllers\BaseCrudController;
use App\Models\Document;
use App\Services\ActivityLogger;
use App\Services\DocumentService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Modules\Activity\Entities\Activity;
use Modules\Activity\Http\Requests\ActivityRequest;
use Modules\Activity\Http\Requests\UploadActivityDocumentRequest;
use Modules\Activity\Http\Requests\UploadActivityGalleryImageRequest;
use Modules\Activity\Repositories\Contracts\ActivityRepositoryInterface;
use Modules\Activity\Services\ActivityService;
use Modules\Activity\Transformers\ActivityListResource;
use Modules\Activity\Transformers\ActivityResource;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ActivityController extends BaseCrudController
{
    protected $modelClass = Activity::class;

    protected $resourceClass = ActivityResource::class;

    protected $requestClass = ActivityRequest::class;

    protected $resourceName = 'activity';

    protected $resourceNamePlural = 'activities';

    public function __construct(ActivityRepositoryInterface $repository, protected ActivityService $activityService, protected DocumentService $documentService)
    {
        parent::__construct($repository);
    }

    public function index(Request $request): JsonResponse
    {
        $this->authorize('viewAny', $this->modelClass);

        try {
            $perPage = $request->get('per_page', 10) ?? 10;
            $perPage = min($perPage, 1000);
            $search = (string) ($request->get('search', '') ?? '');
            $sortBy = (string) ($request->get('sort_by', 'start_datetime') ?? 'start_datetime');
            $sortOrder = (string) ($request->get('sort_order', 'desc') ?? 'desc');

            $items = $this->getIndexData($request, $perPage, $search, $sortBy, $sortOrder);

            if ($items instanceof LengthAwarePaginator) {
                return response()->json([
                    'success' => true,
                    'data' => ActivityListResource::collection($items->items()),
                    'pagination' => [
                        'current_page' => $items->currentPage(),
                        'per_page' => $items->perPage(),
                        'total' => $items->total(),
                        'last_page' => $items->lastPage(),
                        'from' => $items->firstItem(),
                        'to' => $items->lastItem(),
                        'has_more_pages' => $items->hasMorePages(),
                    ],
                    'message' => $this->getSuccessMessage('index'),
                ]);
            }

            return response()->json([
                'success' => true,
                'data' => ActivityListResource::collection($items),
                'message' => $this->getSuccessMessage('index'),
            ]);
        } catch (Exception $e) {
            Log::error("Error fetching {$this->resourceNamePlural}: ".$e->getMessage());

            return $this->errorResponse($this->getErrorMessage('index'), 500);
        }
    }

    protected function getIndexData(Request $request, int $perPage = 10, string $search = '', string $sortBy = 'start_datetime', string $sortOrder = 'desc')
    {
        if ($perPage > 0) {
            return $this->repository->getIndexPaginated($perPage, $search, $sortBy, $sortOrder);
        }

        return $this->repository->all($search);
    }

    public function store(Request $request): JsonResponse
    {
        $this->authorize('create', $this->modelClass);

        try {
            $validated = $this->validateRequest($request);
            $documents = collect($request->file('documents', []))
                ->filter()
                ->all();
            $galleryImages = collect($request->file('gallery', []))
                ->filter()
                ->all();

            $activity = $this->activityService->create(
                $this->prepareStoreData(collect($validated)->except(['documents', 'gallery'])->all()),
                $documents,
                $galleryImages,
            );

            ActivityLogger::log("Created {$this->resourceName}: {$this->getItemName($activity)}", $activity);

            return response()->json([
                'success' => true,
                'data' => new $this->resourceClass($activity),
                'message' => $this->getSuccessMessage('store'),
            ], 201);
        } catch (Exception $e) {
            Log::error("Error creating {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse($this->getErrorMessage('store').': '.$e->getMessage(), 500);
        }
    }

    public function update(Request $request, string $id): JsonResponse
    {
        $this->authorize('update', $this->modelClass);

        try {
            $activity = $this->repository->findById($id);

            if (! $activity) {
                return $this->errorResponse($this->getNotFoundMessage(), 404);
            }

            $validated = $this->validateRequest($request);
            $documents = collect($request->file('documents', []))
                ->filter()
                ->all();
            $galleryImages = collect($request->file('gallery', []))
                ->filter()
                ->all();

            $activity = $this->activityService->update(
                $activity,
                $this->prepareUpdateData(collect($validated)->except(['documents', 'gallery'])->all(), $activity),
                $documents,
                $galleryImages,
            );

            ActivityLogger::log("Updated {$this->resourceName}: {$this->getItemName($activity)}", $activity);

            return response()->json([
                'success' => true,
                'data' => new $this->resourceClass($activity),
                'message' => $this->getSuccessMessage('update'),
            ]);
        } catch (Exception $e) {
            Log::error("Error updating {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse($this->getErrorMessage('update').': '.$e->getMessage(), 500);
        }
    }

    protected function loadShowRelations($item)
    {
        return $this->repository->findByIdWithRelations($item->id) ?? $item;
    }

    protected function getItemName($item): string
    {
        return $item->title ?? $item->id;
    }

    protected function checkDependencies($activity): ?JsonResponse
    {
        if ($activity->reports()->count() > 0) {
            return response()->json([
                'success' => false,
                'message' => 'Aktiviti tidak boleh dipadam kerana masih mempunyai laporan.',
            ], 422);
        }

        return null;
    }

    public function uploadDocument(
        UploadActivityDocumentRequest $request,
        Activity $activity
    ): JsonResponse {
        $this->authorize('update', $this->modelClass);

        try {
            $activity = $this->activityService->uploadDocument(
                $activity,
                $request->file('file'),
                $request->validated('type', 'general'),
                $request->validated('description'),
            );

            return response()->json([
                'success' => true,
                'message' => 'Dokumen berjaya dimuat naik.',
                'data' => new ActivityResource($activity),
            ]);
        } catch (Exception $e) {
            Log::error("Error uploading document for {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse('Gagal memuat naik dokumen: '.$e->getMessage(), 500);
        }
    }

    public function uploadGalleryImage(UploadActivityGalleryImageRequest $request, Activity $activity): JsonResponse {
        $this->authorize('update', $this->modelClass);

        try {
            $activity = $this->activityService->uploadGalleryImage(
                $activity,
                $request->file('file'),
                $request->validated('description'),
            );

            return response()->json([
                'success' => true,
                'message' => 'Imej galeri berjaya dimuat naik.',
                'data' => new ActivityResource($activity),
            ]);
        } catch (Exception $e) {
            Log::error("Error uploading gallery image for {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse('Gagal memuat naik imej galeri: '.$e->getMessage(), 500);
        }
    }

    public function downloadDocument(Activity $activity, string $documentId): StreamedResponse
    {
        $this->authorize('view', $this->modelClass);

        $document = $activity->documents()
            ->where('type', '!=', Activity::GALLERY_DOCUMENT_TYPE)
            ->findOrFail($documentId);

        return $this->documentService->downloadDocument($document->id);
    }

    public function serveGalleryImage(Activity $activity, string $imageId): BinaryFileResponse
    {
        $this->authorize('view', $this->modelClass);

        $document = $activity->galleryImages()->findOrFail($imageId);

        $disk = Storage::disk(Document::STORAGE_DISK);

        if (! $disk->exists($document->path)) {
            abort(404, 'File not found');
        }

        return response()->file($disk->path($document->path), [
            'Content-Type' => $document->mime_type ?? 'application/octet-stream',
            'Content-Disposition' => 'inline; filename="'.$document->name.'"',
        ]);
    }

    public function deleteDocument(Activity $activity, string $documentId): JsonResponse
    {
        $this->authorize('update', $this->modelClass);

        try {
            $activity->documents()
                ->where('type', '!=', Activity::GALLERY_DOCUMENT_TYPE)
                ->findOrFail($documentId);

            $activity->deleteDocument($documentId);
            $activity = $this->repository->findByIdWithRelations($activity->id);

            return response()->json([
                'success' => true,
                'message' => 'Dokumen berjaya dipadam.',
                'data' => new ActivityResource($activity),
            ]);
        } catch (Exception $e) {
            Log::error("Error deleting document for {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse('Gagal memadam dokumen: '.$e->getMessage(), 500);
        }
    }

    public function deleteGalleryImage(Activity $activity, string $imageId): JsonResponse
    {
        $this->authorize('update', $this->modelClass);

        try {
            $activity->galleryImages()->findOrFail($imageId);
            $activity->deleteDocument($imageId);
            $activity = $this->repository->findByIdWithRelations($activity->id);

            return response()->json([
                'success' => true,
                'message' => 'Imej galeri berjaya dipadam.',
                'data' => new ActivityResource($activity),
            ]);
        } catch (Exception $e) {
            Log::error("Error deleting gallery image for {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse('Gagal memadam imej galeri: '.$e->getMessage(), 500);
        }
    }

    public function destroy(string $id): JsonResponse
    {
        $this->authorize('deleteAny', $this->modelClass);

        try {
            $activity = $this->repository->findByIdWithRelations($id);

            if (! $activity) {
                return $this->errorResponse($this->getNotFoundMessage(), 404);
            }

            $dependencyCheck = $this->checkDependencies($activity);
            if ($dependencyCheck !== null) {
                return $dependencyCheck;
            }

            $activity->documents->each(
                fn ($document) => $this->documentService->deleteDocument($document->id)
            );

            $this->repository->delete($id);

            ActivityLogger::log("Deleted {$this->resourceName}: {$this->getItemName($activity)}", $activity);

            return response()->json([
                'success' => true,
                'message' => $this->getSuccessMessage('destroy'),
            ]);
        } catch (Exception $e) {
            Log::error("Error deleting {$this->resourceName}: ".$e->getMessage());

            return $this->errorResponse($this->getErrorMessage('destroy').': '.$e->getMessage(), 500);
        }
    }
}
