<?php

namespace Modules\Feedback\Http\Controllers;

use App\Http\Controllers\BaseCrudController;
use App\Models\Document;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Modules\Feedback\Entities\Feedback;
use Modules\Feedback\Http\Requests\FeedbackRequest;
use Modules\Feedback\Repositories\Contracts\FeedbackRepositoryInterface;
use Modules\Feedback\Transformers\FeedbackResource;
use Modules\Feedback\Notifications\FeedbackNotification;
use Modules\Auth\Entities\User;
use Laravel\Sanctum\PersonalAccessToken;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class FeedbackController extends BaseCrudController
{
    protected $modelClass = Feedback::class;
    protected $resourceClass = FeedbackResource::class;
    protected $requestClass = FeedbackRequest::class;
    protected $resourceName = 'maklum balas';
    protected $resourceNamePlural = 'maklum balas';

    public function __construct(FeedbackRepositoryInterface $repository)
    {
        parent::__construct($repository);
    }

    /**
     * Override getIndexData to handle filters
     */
    protected function getIndexData(Request $request, int $perPage = 10, string $search = '', string $sortBy = 'id', string $sortOrder = 'asc')
    {
        // Extract filter parameters from request
        $filters = [
            'type' => $request->get('type'),
            'status' => $request->get('status'),
            'priority' => $request->get('priority'),
        ];

        // Always try paginated methods first when perPage is specified
        if ($perPage > 0) {
            if (method_exists($this->repository, 'getAllWithRelationsPaginated')) {
                return $this->repository->getAllWithRelationsPaginated($perPage, $search, $sortBy, $sortOrder, $filters);
            }

            if (method_exists($this->repository, 'getAllPaginated')) {
                return $this->repository->getAllPaginated($perPage, $search, $sortBy, $sortOrder, $filters);
            }
        }

        // Fallback to non-paginated methods
        if (method_exists($this->repository, 'getAllWithRelations')) {
            return $this->repository->getAllWithRelations($search, $sortBy, $sortOrder);
        }

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

    /**
     * Store a newly created resource in storage (public access)
     */
    public function store(Request $request): JsonResponse
    {
        // Skip authorization for public feedback submission
        try {
            $validated = $this->validateRequest($request);
            $data = $this->prepareStoreData($validated);

            $item = $this->repository->create($data);

            $this->uploadAttachments($item, $request);

            // Load relationships
            $item->load(['user', 'documents']);

            // Send notification to admins
            $this->notifyAdminsForNewFeedback($item);

            return response()->json([
                'success' => true,
                'data' => new $this->resourceClass($item),
                'message' => 'Maklum balas berjaya dihantar. Terima kasih!',
            ], 201);

        } catch (Exception $e) {
            Log::error("Error creating {$this->resourceName}: ".$e->getMessage());

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

    /**
     * Prepare data for store method
     */
    protected function prepareStoreData(array $validated): array
    {
        $data = $validated;
        
        // Check if user is authenticated by looking for the Authorization header
        $authHeader = request()->header('Authorization');
        if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
            // Try to authenticate the user manually
            $token = str_replace('Bearer ', '', $authHeader);
            $personalAccessToken = PersonalAccessToken::findToken($token);
            
            if ($personalAccessToken) {
                $data['user_id'] = $personalAccessToken->tokenable_id;
            } else {
                $data['user_id'] = null;
            }
        } else {
            // User is not authenticated
            $data['user_id'] = null;
        }
        
        $data['browser_info'] = $this->getBrowserInfo(request());

        unset($data['images'], $data['videos']);

        return $data;
    }

    /**
     * Upload image and video attachments via HasDocuments.
     */
    protected function uploadAttachments(Feedback $feedback, Request $request): void
    {
        if ($request->hasFile('images')) {
            foreach ($request->file('images') as $image) {
                $feedback->uploadDocument($image, Feedback::IMAGE_DOCUMENT_TYPE);
            }
        }

        if ($request->hasFile('videos')) {
            foreach ($request->file('videos') as $video) {
                $feedback->uploadDocument($video, Feedback::VIDEO_DOCUMENT_TYPE);
            }
        }
    }

    /**
     * Prepare data for update method
     */
    protected function prepareUpdateData(array $validated, $feedback): array
    {
        $data = $validated;

        unset($data['images'], $data['videos']);

        // Set resolved_at timestamp when status changes to resolved
        if (isset($data['status']) && $data['status'] === 'resolved') {
            $data['resolved_at'] = now();
        } elseif (isset($data['status']) && $data['status'] !== 'resolved') {
            $data['resolved_at'] = null;
        }

        return $data;
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id): JsonResponse
    {
        $this->authorize('update', $this->modelClass);

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

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

            $validated = $this->validateRequest($request);
            $data = $this->prepareUpdateData($validated, $item);

            $item->update($data);
            $item->load(['user', 'assignedUser', 'documents']);

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

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

    /**
     * Load relations for show method
     */
    protected function loadShowRelations($item)
    {
        return $item->load(['user', 'assignedUser', 'documents']);
    }

    /**
     * Check dependencies before deletion
     */
    protected function checkDependencies($feedback): ?\Illuminate\Http\JsonResponse
    {
        foreach ($feedback->documents as $document) {
            $feedback->deleteDocument($document->id);
        }

        return null;
    }

    /**
     * Stream a feedback attachment inline (image/video preview).
     */
    public function serveDocument(string $id, string $documentId): BinaryFileResponse
    {
        $this->authorize('view', $this->modelClass);

        $feedback = Feedback::findOrFail($id);
        $document = $feedback->documents()->findOrFail($documentId);

        $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.'"',
        ]);
    }

    /**
     * Get feedback statistics
     */
    public function statistics(): JsonResponse
    {
        $stats = [
            'total' => Feedback::count(),
            'open' => Feedback::open()->count(),
            'resolved' => Feedback::resolved()->count(),
            'by_type' => Feedback::selectRaw('type, COUNT(*) as count')
                ->groupBy('type')
                ->pluck('count', 'type'),
            'by_priority' => Feedback::selectRaw('priority, COUNT(*) as count')
                ->groupBy('priority')
                ->pluck('count', 'priority'),
            'by_status' => Feedback::selectRaw('status, COUNT(*) as count')
                ->groupBy('status')
                ->pluck('count', 'status'),
        ];

        return response()->json([
            'success' => true,
            'data' => $stats
        ]);
    }

    /**
     * Get user's own feedback
     */
    public function myFeedback(Request $request): JsonResponse
    {
        $query = Feedback::where('user_id', auth()->id())
            ->with(['assignedUser', 'documents'])
            ->orderBy('created_at', 'desc');

        // Apply filters
        if ($request->has('status') && $request->status) {
            $query->ofStatus($request->status);
        }

        if ($request->has('type') && $request->type) {
            $query->ofType($request->type);
        }

        $perPage = $request->get('per_page', 15);
        $feedback = $query->paginate($perPage);

        return response()->json([
            'success' => true,
            'data' => FeedbackResource::collection($feedback->items()),
            'meta' => [
                'current_page' => $feedback->currentPage(),
                'last_page' => $feedback->lastPage(),
                'per_page' => $feedback->perPage(),
                'total' => $feedback->total(),
            ]
        ]);
    }

    /**
     * Get browser information from request
     */
    private function getBrowserInfo(Request $request): array
    {
        return [
            'user_agent' => $request->userAgent(),
            'ip_address' => $request->ip(),
            'referer' => $request->header('referer'),
            'accept_language' => $request->header('accept-language'),
            'screen_resolution' => $request->input('screen_resolution'),
            'viewport_size' => $request->input('viewport_size'),
            'timezone' => $request->input('timezone'),
        ];
    }

    /**
     * Notify admins about new feedback submission
     */
    private function notifyAdminsForNewFeedback(Feedback $feedback): void
    {
        try {
            $adminRoles = ['PENTADBIR', 'PS 2 KJC', 'PS 2 ALAT'];
            
            // Get users with specific roles plus admins (PENTADBIR and DEVELOPER)
            $adminUsers = $this->getUsersWithRolesAndAdmins($adminRoles);

            $sender = auth()->user() ?? $feedback->user; // Use current user as sender, or feedback submitter if no auth

            foreach ($adminUsers as $admin) {
                try {
                    $admin->notify(new FeedbackNotification($feedback, $sender));
                } catch (Exception $e) {
                    Log::error('Failed to send feedback notification: '.$e->getMessage());
                }
            }
        } catch (Exception $e) {
            Log::error('Failed to notify admins for new feedback: '.$e->getMessage());
        }
    }
}