<?php

namespace App\Http\Controllers;

use App\Models\Project;
use App\Models\ProjectMediaFile;
use App\Models\ProjectTag;
use App\Models\ProjectTicket;
use App\Models\Task;
use App\Models\TaskTag;
use App\Models\User;
use App\Traits\ApiResponse;
use App\Traits\SendsPushNotifications;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Throwable;

class ProjectController extends Controller
{
    use ApiResponse, SendsPushNotifications;

    private function projectNotificationRecipientIds(Project $project, array $additionalRecipientIds = []): array
    {
        $project->loadMissing('project_assignees');

        return collect([$project->created_by, $project->team_leader])
            ->merge($project->project_assignees->pluck('user_id'))
            ->merge($additionalRecipientIds)
            ->filter()
            ->unique()
            ->values()
            ->all();
    }

    private function sendProjectNotifications(Project $project, $actor, string $event, array $additionalRecipientIds = [], array $extraData = [], string $logPrefix = 'project notification'): void
    {
        try {
            $recipientIds = collect($this->projectNotificationRecipientIds($project, $additionalRecipientIds))
                ->reject(fn ($userId) => $userId === $actor->id)
                ->values();

            if ($recipientIds->isEmpty()) {
                return;
            }

            $messages = [
                'created' => ['New Project Created', "Project created: {$project->project_name}"],
                'updated' => ['Project Updated', "Project updated: {$project->project_name}"],
                'deleted' => ['Project Deleted', "Project deleted: {$project->project_name}"],
                'completed' => ['Project Completed', "Project completed: {$project->project_name}"],
            ];

            [$title, $body] = $messages[$event] ?? ['Project Updated', "Project updated: {$project->project_name}"];

            User::whereIn('id', $recipientIds)->get()->each(function (User $user) use ($title, $body, $event, $project, $extraData) {
                $this->notifyUser($user, $title, $body, array_merge([
                    'type' => 'project_' . $event,
                    'project_id' => $project->id,
                    'project_name' => $project->project_name,
                ], $extraData));
            });
        } catch (Throwable $e) {
            Log::error("Failed to send {$logPrefix}: " . $e->getMessage());
        }
    }

    private function sendProjectMediaNotifications(Project $project, ProjectMediaFile $mediaFile, $actor, string $event): void
    {
        $messages = [
            'uploaded' => ['Project File Uploaded', "New file uploaded to: {$project->project_name}"],
            'deleted' => ['Project File Deleted', "File deleted from: {$project->project_name}"],
        ];

        [$title, $body] = $messages[$event] ?? ['Project File Updated', "File updated for: {$project->project_name}"];

        $this->sendCustomProjectNotifications(
            $project,
            $actor,
            $title,
            $body,
            'project_media_' . $event,
            [
                'media_file_id' => $mediaFile->id,
                'file_name' => $mediaFile->file_name,
            ],
            'project media notification'
        );
    }

    private function sendProjectTicketNotifications(Project $project, ProjectTicket $ticket, $actor, string $event): void
    {
        $messages = [
            'created' => ['Project Ticket Created', "New ticket created for: {$project->project_name}"],
            'updated' => ['Project Ticket Updated', "Ticket updated for: {$project->project_name}"],
            'deleted' => ['Project Ticket Deleted', "Ticket deleted from: {$project->project_name}"],
        ];

        [$title, $body] = $messages[$event] ?? ['Project Ticket Updated', "Ticket updated for: {$project->project_name}"];

        $this->sendCustomProjectNotifications(
            $project,
            $actor,
            $title,
            $body,
            'project_ticket_' . $event,
            [
                'ticket_id' => $ticket->id,
                'ticket_name' => $ticket->name,
            ],
            'project ticket notification'
        );
    }

    private function sendCustomProjectNotifications(Project $project, $actor, string $title, string $body, string $type, array $extraData = [], string $logPrefix = 'project notification'): void
    {
        try {
            $recipientIds = collect($this->projectNotificationRecipientIds($project))
                ->reject(fn ($userId) => $userId === $actor->id)
                ->values();

            if ($recipientIds->isEmpty()) {
                return;
            }

            User::whereIn('id', $recipientIds)->get()->each(function (User $user) use ($title, $body, $type, $project, $extraData) {
                $this->notifyUser($user, $title, $body, array_merge([
                    'type' => $type,
                    'project_id' => $project->id,
                    'project_name' => $project->project_name,
                ], $extraData));
            });
        } catch (Throwable $e) {
            Log::error("Failed to send {$logPrefix}: " . $e->getMessage());
        }
    }

    private function formatTaskResponse($task)
    {
        $task->load([
            'creator:id,name,image_path',
            'assignee:id,name,image_path',
            'lead:id,name',
            'tag.tag',

            'active_task_query:id,task_id,created_by,assigned_to,query,is_completed,completed_at,created_at,updated_at',
            'active_task_query.creator:id,name,image_path',
            'active_task_query.assignee:id,name,image_path',

            'active_logs:id,task_id,created_by,log,duration_seconds,created_at,updated_at',
            'active_logs.creator:id,name,image_path'
        ]);

        return [
            'id' => $task->id,
            'name' => $task->name,
            'description' => $task->description,
            'priority' => $task->priority,
            'type' => $task->type,
            'start_date' => $task->start_date,
            'due_date' => $task->due_date,
            'is_completed' => $task->is_completed,
            'completed_at' => $task->completed_at,
            'creator' => $task->creator ? [
                'id' => $task->creator->id,
                'name' => $task->creator->name,
                'image_path' => $task->creator->image_path ? Storage::disk('public')->url($task->creator->image_path) : null,
            ] : null,
            'assignee' => $task->assignee ? [
                'id' => $task->assignee->id,
                'name' => $task->assignee->name,
                'image_path' => $task->assignee->image_path ? Storage::disk('public')->url($task->assignee->image_path) : null,
            ] : null,
            'lead' => $task->lead ? [
                'id' => $task->lead->id,
                'name' => $task->lead->name,
            ] : null,
            'tags' => $task->tag->map(function ($taskTag) {
                return [
                    'id' => $taskTag->tag->id,
                    'name' => $taskTag->tag->name,
                ];
            }),
            'queries' => $task->active_task_query->map(function ($query) {
                return [
                    'id' => $query->id,
                    'query' => $query->query,
                    'is_completed' => $query->is_completed,
                    'completed_at' => $query->completed_at,

                    'creator' => $query->creator ? [
                        'id' => $query->creator->id,
                        'name' => $query->creator->name,
                        'image_path' => $query->creator->image_path
                            ? Storage::disk('public')->url($query->creator->image_path)
                            : null,
                    ] : null,

                    'assignee' => $query->assignee ? [
                        'id' => $query->assignee->id,
                        'name' => $query->assignee->name,
                        'image_path' => $query->assignee->image_path
                            ? Storage::disk('public')->url($query->assignee->image_path)
                            : null,
                    ] : null,

                    'created_at' => $query->created_at,
                    'updated_at' => $query->updated_at,
                ];
            }),
            'logs' => $task->active_logs->map(function ($log) {
                return [
                    'id' => $log->id,
                    'log' => $log->log,
                    'duration_seconds' => $log->duration_seconds,

                    'creator' => $log->creator ? [
                        'id' => $log->creator->id,
                        'name' => $log->creator->name,
                        'image_path' => $log->creator->image_path
                            ? Storage::disk('public')->url($log->creator->image_path)
                            : null,
                    ] : null,

                    'created_at' => $log->created_at,
                    'updated_at' => $log->updated_at,
                ];
            }),
            'created_at' => $task->created_at,
            'updated_at' => $task->updated_at,
        ];
    }

    private function formatTicketResponse($ticket)
    {
        $ticket->load([
            'creator:id,name,email,image_path',
        ]);

        return [
            'id' => $ticket->id,
            'name' => $ticket->name,
            'description' => $ticket->description,
            'priority' => $ticket->priority,
            'status' => $ticket->status,
            'created_at' => $ticket->created_at,
            'updated_at' => $ticket->due_date,
            'creator' => $ticket->creator ? [
                'id' => $ticket->creator->id,
                'name' => $ticket->creator->name,
                'email' => $ticket->creator->email,
                'image_path' => $ticket->creator->image_path ? Storage::disk('public')->url($ticket->creator->image_path) : null,
            ] : null,
        ];
    }

    private function formatProjectResponse($project)
    {
        $project->load([
            'lead',
            'creator:id,name,email,image_path',
            'team_lead:id,name,email,image_path',
            'project_assignees.user:id,name,email,image_path',
            'project_tags.tag:id,name,color'
        ]);

        return [
            'id' => $project->id,
            'project_name' => $project->project_name,
            'project_description' => $project->project_description,
            'project_time' => $project->project_time,
            'project_cost' => $project->project_cost,
            'start_date' => $project->start_date,
            'end_date' => $project->end_date,
            'priority' => $project->priority,
            'status' => $project->status,
            'lead' => $project->lead instanceof Model ? [
                'id' => $project->lead->id,
                'name' => $project->lead->name,
            ] : null,
            'creator' => $project->creator instanceof Model ? [
                'id' => $project->creator->id,
                'name' => $project->creator->name,
                'email' => $project->creator->email,
                'image_path' => $project->creator->image_path ? Storage::disk('public')->url($project->creator->image_path) : null,
            ] : null,
            'team_lead_detail' => $project->team_lead instanceof Model ? [
                'id' => $project->team_lead->id,
                'name' => $project->team_lead->name,
                'email' => $project->team_lead->email,
                'image_path' => $project->team_lead->image_path ? Storage::disk('public')->url($project->team_lead->image_path) : null,
            ] : null,
            'project_assignees' => $project->project_assignees->map(function ($assignee) {
                return $assignee->user ? [
                        'id' => $assignee->user->id,
                        'name' => $assignee->user->name,
                        'email' => $assignee->user->email,
                        'image_path' => $assignee->user->image_path ? Storage::disk('public')->url($assignee->user->image_path) : null,
                    ] : null;
            }),
            'project_tags' => $project->project_tags->map(function ($projectTag) {
                return [
                    'id' => $projectTag->tag->id,
                    'name' => $projectTag->tag->name,
                    'color' => $projectTag->tag->color,
                ];
            }),
            'created_at' => $project->created_at,
            'updated_at' => $project->updated_at,
        ];
    }

    public function create_project(Request $request)
    {
        $user = $request->user();

        $hasCreate = $user->can('project_management.project.create');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasCreate && !$hasOverride) {
            return $this->error('Forbidden', 403);
        }

        $request->validate([
            'lead_id' => ['required', 'exists:leads,id'],
            'name' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'time' => ['nullable', 'string', 'max:255'],
            'cost' => ['nullable', 'string', 'max:255'],
            'start_date' => ['required', 'date', 'after_or_equal:today'],
            'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
            'priority' => ['required', 'string', 'in:low,medium,high'],
            'team_members' => ['required', 'array'],
            'team_members.*' => ['required', 'exists:users,id'],
            'tags' => ['nullable', 'array'],
            'tags.*' => ['required', 'exists:tags,id'],
        ]);

        try {
            DB::beginTransaction();

            // Determine status based on start date
            $startDate = Carbon::parse($request->start_date);
            $today = Carbon::today();

            $status = 'upcoming'; // default
            if ($startDate->eq($today)) {
                $status = 'in_progress';
            }

            $project = Project::create([
                'lead_id' => $request->lead_id,
                'created_by' => $user->id,
                'team_leader' => $user->id,
                'project_name' => $request->name,
                'project_description' => $request->description,
                'project_time' => $request->time,
                'project_cost' => $request->cost,
                'start_date' => $request->start_date,
                'end_date' => $request->end_date,
                'priority' => $request->priority,
                'status' => $status,
                'created_at' => now(),
            ]);

            // Create project assignees if team members provided
            if ($request->has('team_members') && is_array($request->team_members)) {
                foreach ($request->team_members as $memberId) {
                    $project->project_assignees()->create([
                        'project_id' => $project->id,
                        'user_id' => $memberId,
                        'created_at' => now(),
                    ]);
                }
            }

            // Create project tags if tags provided
            if ($request->has('tags') && is_array($request->tags)) {
                foreach ($request->tags as $tagId) {
                    ProjectTag::create([
                        'project_id' => $project->id,
                        'tag_id' => $tagId,
                        'created_at' => now(),
                    ]);
                }
            }


            // Log Activity for PROJECT creation
            activity('project_management')
                ->performedOn($project)
                ->causedBy($user)
                ->withProperties([
                    'project_name' => $project->project_name,
                    'lead_id' => $project->lead_id,
                    'team_leader' => $project->team_leader,
                    'priority' => $project->priority,
                    'status' => $project->status,
                    'start_date' => $project->start_date,
                    'end_date' => $project->end_date,
                    'team_members' => $request->team_members ?? [],
                    'tags' => $request->tags ?? [],
                    'ip' => $request->ip(),
                    'user_agent' => $request->userAgent(),
                ])
                ->log("Project '{$project->project_name}' created by {$user->name}.");

            DB::commit();

            $this->sendProjectNotifications($project->fresh(), $user, 'created', $request->team_members ?? [], [
                'lead_id' => $project->lead_id,
            ], 'project creation notification');

            return $this->success('Project created successfully', $this->formatProjectResponse($project));
        } catch (Throwable $th) {
            DB::rollBack();
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_project_tasks(Request $request)
    {
        $user = $request->user();

        $hasView = $user->can('task_management.task.view');
        $hasOverride = $user->can('task_management.task.override');

        if (!$hasView && !$hasOverride) {
            return $this->error('Forbidden', 403);
        }

        $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
        ]);

        try {
            $paginatedTasks = Task::where('is_deleted', false)
                ->where('project_id', $request->project_id)
                ->orderBy('created_at', 'desc')
                ->paginate(15);

            $tasks = $paginatedTasks->getCollection()->map(function ($task) {
                return $this->formatTaskResponse($task);
            });

            return $this->success('Tasks retrieved successfully.', [
                'data' => $tasks,
                'current_page' => $paginatedTasks->currentPage(),
                'next_page' => $paginatedTasks->hasMorePages()
                    ? $paginatedTasks->currentPage() + 1
                    : null,
                'last_page' => $paginatedTasks->lastPage(),
                'total_items' => $paginatedTasks->total(),
                'per_page' => $paginatedTasks->perPage(),
            ]);

        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function update_project(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
            // Main project fields
            'lead_id' => ['nullable', 'exists:leads,id'],
            'name' => ['nullable', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'priority' => ['nullable', 'string', 'in:low,medium,high'],
            // Team leader
            'team_leader' => ['nullable', 'exists:users,id'],
            // Team members
            'team_members' => ['nullable', 'array'],
            'team_members.*' => ['required', 'exists:users,id'],
            // Tags
            'tags' => ['nullable', 'array'],
            'tags.*' => ['required', 'exists:tags,id'],
            // Cost & Time
            'cost' => ['nullable', 'string', 'max:255'],
            'time' => ['nullable', 'string', 'max:255'],
            // Dates
            'start_date' => ['nullable', 'date'],
            'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
        ]);

        $project = Project::findOrFail($request->project_id);

        $hasUpdate = $user->can('project_management.project.update');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && (!$hasUpdate || $user->id !== $project->team_leader)) {
            return $this->error('Forbidden', 403);
        }

        try {
            DB::beginTransaction();

            $logs = [];

            // Update main project fields
            $projectUpdateData = [];

            if ($request->has('lead_id')) {
                $oldLeadId = $project->lead_id;
                $projectUpdateData['lead_id'] = $request->lead_id;
                if ($oldLeadId != $request->lead_id) {
                    $logs[] = "Lead changed from {$oldLeadId} to {$request->lead_id}";
                }
            }

            if ($request->has('name')) {
                $oldName = $project->project_name;
                $projectUpdateData['project_name'] = $request->name;
                if ($oldName != $request->name) {
                    $logs[] = "Name changed from '{$oldName}' to '{$request->name}'";
                }
            }

            if ($request->has('description')) {
                $projectUpdateData['project_description'] = $request->description;
                $logs[] = 'Description updated';
            }

            if ($request->has('priority')) {
                $oldPriority = $project->priority;
                $projectUpdateData['priority'] = $request->priority;
                if ($oldPriority != $request->priority) {
                    $logs[] = "Priority changed from {$oldPriority} to {$request->priority}";
                }
            }

            if ($request->has('cost')) {
                $oldCost = $project->project_cost;
                $projectUpdateData['project_cost'] = $request->cost;
                if ($oldCost != $request->cost) {
                    $logs[] = "Cost changed from '{$oldCost}' to '{$request->cost}'";
                }
            }

            if ($request->has('time')) {
                $oldTime = $project->project_time;
                $projectUpdateData['project_time'] = $request->time;
                if ($oldTime != $request->time) {
                    $logs[] = "Time changed from '{$oldTime}' to '{$request->time}'";
                }
            }

            if ($request->has('start_date')) {
                $oldStartDate = $project->start_date;
                $projectUpdateData['start_date'] = $request->start_date;

                // Update status based on new start date
                $startDate = Carbon::parse($request->start_date);
                $today = Carbon::today();
                $newStatus = $startDate->eq($today) ? 'in_progress' : 'upcoming';
                $projectUpdateData['status'] = $newStatus;

                $logs[] = "Start date changed from '{$oldStartDate}' to '{$request->start_date}', status set to {$newStatus}";
            }

            if ($request->has('end_date')) {
                $oldEndDate = $project->end_date;
                $projectUpdateData['end_date'] = $request->end_date;
                if ($oldEndDate != $request->end_date) {
                    $logs[] = "End date changed from '{$oldEndDate}' to '{$request->end_date}'";
                }
            }

            if (!empty($projectUpdateData)) {
                $projectUpdateData['updated_at'] = now();
                $project->update($projectUpdateData);
            }

            // Update team leader
            if ($request->has('team_leader')) {
                $oldTeamLeader = $project->team_leader;
                if ($oldTeamLeader != $request->team_leader) {
                    $project->team_leader = $request->team_leader;
                    $project->updated_at = now();
                    $project->save();
                    $logs[] = "Team leader changed from {$oldTeamLeader} to {$request->team_leader}";
                }
            }

            // Update team members
            if ($request->has('team_members') && is_array($request->team_members)) {
                $oldTeamMembers = $project->project_assignees()->pluck('user_id')->toArray();
                sort($oldTeamMembers);
                $newTeamMembers = $request->team_members;
                sort($newTeamMembers);

                if ($oldTeamMembers != $newTeamMembers) {
                    $project->project_assignees()->delete();
                    foreach ($request->team_members as $teamMemberId) {
                        $project->project_assignees()->create([
                            'user_id' => $teamMemberId,
                            'created_at' => now()
                        ]);
                    }
                    $logs[] = 'Team members updated';
                }
            }

            // Update tags
            if ($request->has('tags') && is_array($request->tags)) {
                $oldTags = $project->project_tags()->pluck('tag_id')->toArray();
                sort($oldTags);
                $newTags = $request->tags;
                sort($newTags);

                if ($oldTags != $newTags) {
                    $project->project_tags()->delete();
                    foreach ($request->tags as $tagId) {
                        $project->project_tags()->create([
                            'tag_id' => $tagId,
                            'created_at' => now()
                        ]);
                    }
                    $logs[] = 'Tags updated';
                }
            }

            // Log Activity if any changes were made
            if (!empty($logs)) {
                activity('project_management')
                    ->performedOn($project)
                    ->causedBy($user)
                    ->withProperties([
                        'project_name' => $project->project_name,
                        'changes' => $logs,
                        'ip' => $request->ip(),
                        'user_agent' => $request->userAgent(),
                    ])
                    ->log("Project '{$project->project_name}' updated by {$user->name}: " . implode('; ', $logs));
            }

            DB::commit();

            $freshProject = $project->fresh();

            if (!empty($logs)) {
                $this->sendProjectNotifications($freshProject, $user, 'updated', [], [
                    'changes' => $logs,
                ], 'project update notification');
            }

            return $this->success('Project updated successfully', $this->formatProjectResponse($freshProject));
        } catch (Throwable $th) {
            DB::rollBack();
            return $this->error($th->getMessage(), 500);
        }
    }

    public function delete_project(Request $request)
    {
        $user = $request->user();

        $project = Project::findOrFail($request->project_id);

        $hasDelete = $user->can('project_management.project.delete');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && (!$hasDelete || $user->id !== $project->team_leader)) {
            return $this->error('Forbidden', 403);
        }

        $project->is_deleted = 1;
        $project->deleted_at = now();
        $project->save();

        activity('project_management')
            ->performedOn($project)
            ->causedBy($user)
            ->withProperties([
                'project_name' => $project->project_name,
                'lead_id' => $project->lead_id,
                'team_leader' => $project->team_leader,
                'priority' => $project->priority,
                'status' => $project->status,
                'deleted_at' => $project->deleted_at,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
            ])
            ->log("Project '{$project->project_name}' deleted by {$user->name}.");

        $this->sendProjectNotifications($project, $user, 'deleted', [], [], 'project delete notification');

        return $this->success('Project deleted successfully');
    }

    public function complete_project(Request $request)
    {
        $user = $request->user();

        $project = Project::findOrFail($request->project_id);

        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && $user->id !== $project->team_leader) {
            return $this->error('Forbidden', 403);
        }

        $previousStatus = $project->status;
        $project->status = 'completed';
        $project->updated_at = now();
        $project->save();

        activity('project_management')
            ->performedOn($project)
            ->causedBy($user)
            ->withProperties([
                'project_name' => $project->project_name,
                'lead_id' => $project->lead_id,
                'team_leader' => $project->team_leader,
                'priority' => $project->priority,
                'previous_status' => $previousStatus,
                'new_status' => 'completed',
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
            ])
            ->log("Project '{$project->project_name}' completed by {$user->name}.");

        $this->sendProjectNotifications($project, $user, 'completed', [], [
            'previous_status' => $previousStatus,
        ], 'project complete notification');

        return $this->success('Project completed successfully');
    }

    public function upload_project_media_file(Request $request)
    {
        $user = $request->user();

        $validate = $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
            'file' => ['required', 'file', 'mimes:jpg,jpeg,png,pdf,doc,docx,zip,webp', 'max:20480'], // 20MB max
        ]);

        $project = Project::findOrFail($request->project_id);

        $hasOverride = $user->can('project_management.project.override');

        // Check if user is team leader or team member
        $isTeamLeader = $user->id === $project->team_leader;
        $isTeamMember = $project->project_assignees()->where('user_id', $user->id)->exists();

        if (!$hasOverride && !$isTeamLeader && !$isTeamMember) {
            return $this->error('Forbidden', 403);
        }

        $disk = 'public';

        try {
            DB::beginTransaction();
            $file = $request->file('file');

            if (!$file || !$file->isValid()) {
                return $this->error($file ? $file->getErrorMessage() : 'No file uploaded', 400);
            }
            $extension = $file->getClientOriginalExtension();
            $fileName = 'project_' . $request->project_id . '_' . time() . '_' . Str::random(6) . '.' . $extension;
            $filePath = $file->storeAs('project_files', $fileName, $disk);

            $mediaFile = ProjectMediaFile::create([
                'project_id' => $project->id,
                'created_by' => $user->id,
                'file_name' => $fileName,
                'file_path' => $filePath,
                'file_type' => $file->getMimeType(),
                'file_size' => $file->getSize(),
                'created_at' => now(),
            ]);

            // Log Activity for MEDIA FILE upload
            activity('project_management')
                ->performedOn($project)
                ->causedBy($user)
                ->withProperties([
                    'project_name' => $project->project_name,
                    'file_name' => $fileName,
                    'file_type' => $file->getMimeType(),
                    'file_size' => $file->getSize(),
                    'file_path' => $filePath,
                    'ip' => $request->ip(),
                    'user_agent' => $request->userAgent(),
                ])
                ->log("Media file '{$fileName}' uploaded for project '{$project->project_name}' by {$user->name}.");

            DB::commit();

            // Load relationships for response
            $mediaFile->load([
                'creator:id,name,email,image_path',
                'project:id,project_name'
            ]);

            // Format response with asset URL for file path
            $formattedMediaFile = [
                'id' => $mediaFile->id,
                'project_id' => $mediaFile->project_id,
                'file_name' => $mediaFile->file_name,
                'file_path' => Storage::disk('public')->url($mediaFile->file_path),
                'file_type' => $mediaFile->file_type,
                'file_size' => (int) $mediaFile->file_size,
                'creator' => $mediaFile->creator ? [
                    'id' => $mediaFile->creator->id,
                    'name' => $mediaFile->creator->name,
                    'email' => $mediaFile->creator->email,
                    'image_path' => $mediaFile->creator->image_path ? Storage::disk('public')->url($mediaFile->creator->image_path) : null,
                ] : null,
                'project' => $mediaFile->project,
                'created_at' => $mediaFile->created_at,
                'updated_at' => $mediaFile->updated_at,
            ];

            return $this->success('Media file uploaded successfully', $formattedMediaFile);

        } catch (Throwable $th) {
            DB::rollBack();
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_all_projects(Request $request)
    {
        $user = $request->user();

        $hasRead = $user->can('project_management.project.read');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasRead && !$hasOverride) {
            return $this->error('Forbidden', 403);
        }

        $request->validate([
            'search' => ['nullable', 'string', 'max:255'],
            'status' => ['nullable', 'string', 'in:upcoming,in_progress,completed'],
            'priority' => ['nullable', 'string', 'in:low,medium,high'],
            'team_leader' => ['nullable', 'exists:users,id'],
            'start_date_from' => ['nullable', 'date'],
            'start_date_to' => ['nullable', 'date', 'after_or_equal:start_date_from'],
            'sort_by' => ['nullable', 'string', 'in:created_at,updated_at,start_date,end_date,project_name,priority'],
            'sort_order' => ['nullable', 'string', 'in:asc,desc'],
        ]);

        try {
            $search = $request->input('search');
            $status = $request->input('status');
            $priority = $request->input('priority');
            $teamLeader = $request->input('team_leader');
            $startDateFrom = $request->input('start_date_from');
            $startDateTo = $request->input('start_date_to');
            $sortBy = $request->input('sort_by', 'created_at');
            $sortOrder = $request->input('sort_order', 'desc');

            // Start building the query
            $query = Project::with([
                'lead:id,name',
                'creator:id,name,email,image_path',
                'team_lead:id,name,email,image_path',
                'project_assignees.user:id,name,email,image_path',
                'project_tags.tag:id,name,color'
            ])->where('is_deleted', 0);

            // Apply search filter
            if ($search) {
                $query->where(function ($q) use ($search) {
                    $q->where('project_name', 'like', "%{$search}%")
                        ->orWhere('project_description', 'like', "%{$search}%");
                });
            }

            // Apply status filter
            if ($status) {
                $query->where('status', $status);
            }

            // Apply priority filter
            if ($priority) {
                $query->where('priority', $priority);
            }

            // Apply team leader filter
            if ($teamLeader) {
                $query->where('team_leader', $teamLeader);
            }

            // Apply date range filter
            if ($startDateFrom) {
                $query->whereDate('start_date', '>=', $startDateFrom);
            }
            if ($startDateTo) {
                $query->whereDate('start_date', '<=', $startDateTo);
            }

            // Apply sorting
            $query->orderBy($sortBy, $sortOrder);

            // Get paginated results
            $projects = $query->paginate(15);

            // Format the projects data using the formatProjectResponse method
            $formattedProjects = $projects->getCollection()->map(function ($project) {
                return $this->formatProjectResponse($project);
            });

            // Create modern pagination response
            $paginationData = [
                'data' => $formattedProjects,
                'current_page' => $projects->currentPage(),
                'next_page' => $projects->hasMorePages()
                    ? $projects->currentPage() + 1
                    : null,
                'last_page' => $projects->lastPage(),
                'total_items' => $projects->total(),
                'per_page' => $projects->perPage(),
                'filters' => [
                    'search' => $search,
                    'status' => $status,
                    'priority' => $priority,
                    'team_leader' => $teamLeader,
                    'start_date_from' => $startDateFrom,
                    'start_date_to' => $startDateTo,
                    'sort_by' => $sortBy,
                    'sort_order' => $sortOrder,
                ]
            ];

            return $this->success('Projects retrieved successfully', $paginationData);

        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_active_projects(Request $request)
    {
        $user = $request->user();

        $hasRead = $user->can('project_management.project.read');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasRead && !$hasOverride) {
            return $this->error('Forbidden', 403);
        }

        $request->validate([
            'search' => ['nullable', 'string', 'max:255'],
            'status' => ['nullable', 'string', 'in:upcoming,in_progress,completed'],
            'priority' => ['nullable', 'string', 'in:low,medium,high'],
            'team_leader' => ['nullable', 'exists:users,id'],
            'start_date_from' => ['nullable', 'date'],
            'start_date_to' => ['nullable', 'date', 'after_or_equal:start_date_from'],
            'sort_by' => ['nullable', 'string', 'in:created_at,updated_at,start_date,end_date,project_name,priority'],
            'sort_order' => ['nullable', 'string', 'in:asc,desc'],
        ]);

        try {
            $search = $request->input('search');
            $status = $request->input('status');
            $priority = $request->input('priority');
            $teamLeader = $request->input('team_leader');
            $startDateFrom = $request->input('start_date_from');
            $startDateTo = $request->input('start_date_to');
            $sortBy = $request->input('sort_by', 'created_at');
            $sortOrder = $request->input('sort_order', 'desc');

            // Start building the query
            $query = Project::with([
                'lead:id,name',
                'creator:id,name,email,image_path',
                'team_lead:id,name,email,image_path',
                'project_assignees.user:id,name,email,image_path',
                'project_tags.tag:id,name,color'
            ])->where('is_deleted', 0)
                ->where('status', ['upcoming', 'in_progress']);

            // Apply search filter
            if ($search) {
                $query->where(function ($q) use ($search) {
                    $q->where('project_name', 'like', "%{$search}%")
                        ->orWhere('project_description', 'like', "%{$search}%");
                });
            }

            // Apply status filter
            if ($status) {
                $query->where('status', $status);
            }

            // Apply priority filter
            if ($priority) {
                $query->where('priority', $priority);
            }

            // Apply team leader filter
            if ($teamLeader) {
                $query->where('team_leader', $teamLeader);
            }

            // Apply date range filter
            if ($startDateFrom) {
                $query->whereDate('start_date', '>=', $startDateFrom);
            }
            if ($startDateTo) {
                $query->whereDate('start_date', '<=', $startDateTo);
            }

            // Apply sorting
            $query->orderBy($sortBy, $sortOrder);

            // Get paginated results
            $projects = $query->paginate(15);

            // Format the projects data using the formatProjectResponse method
            $formattedProjects = $projects->getCollection()->map(function ($project) {
                return $this->formatProjectResponse($project);
            });

            // Create modern pagination response
            $paginationData = [
                'data' => $formattedProjects,
                'current_page' => $projects->currentPage(),
                'next_page' => $projects->hasMorePages()
                    ? $projects->currentPage() + 1
                    : null,
                'last_page' => $projects->lastPage(),
                'total_items' => $projects->total(),
                'per_page' => $projects->perPage(),
                'filters' => [
                    'search' => $search,
                    'status' => $status,
                    'priority' => $priority,
                    'team_leader' => $teamLeader,
                    'start_date_from' => $startDateFrom,
                    'start_date_to' => $startDateTo,
                    'sort_by' => $sortBy,
                    'sort_order' => $sortOrder,
                ]
            ];

            return $this->success('Projects retrieved successfully', $paginationData);

        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_complete_projects(Request $request)
    {
        $user = $request->user();

        $hasRead = $user->can('project_management.project.read');
        $hasOverride = $user->can('project_management.project.override');

        if (!$hasRead && !$hasOverride) {
            return $this->error('Forbidden', 403);
        }

        $request->validate([
            'search' => ['nullable', 'string', 'max:255'],
            'status' => ['nullable', 'string', 'in:upcoming,in_progress,completed'],
            'priority' => ['nullable', 'string', 'in:low,medium,high'],
            'team_leader' => ['nullable', 'exists:users,id'],
            'start_date_from' => ['nullable', 'date'],
            'start_date_to' => ['nullable', 'date', 'after_or_equal:start_date_from'],
            'sort_by' => ['nullable', 'string', 'in:created_at,updated_at,start_date,end_date,project_name,priority'],
            'sort_order' => ['nullable', 'string', 'in:asc,desc'],
        ]);

        try {
            $search = $request->input('search');
            $status = $request->input('status');
            $priority = $request->input('priority');
            $teamLeader = $request->input('team_leader');
            $startDateFrom = $request->input('start_date_from');
            $startDateTo = $request->input('start_date_to');
            $sortBy = $request->input('sort_by', 'created_at');
            $sortOrder = $request->input('sort_order', 'desc');

            // Start building the query
            $query = Project::with([
                'lead:id,name',
                'creator:id,name,email,image_path',
                'team_lead:id,name,email,image_path',
                'project_assignees.user:id,name,email,image_path',
                'project_tags.tag:id,name,color'
            ])->where('is_deleted', 0)
                ->where('status', 'completed');

            // Apply search filter
            if ($search) {
                $query->where(function ($q) use ($search) {
                    $q->where('project_name', 'like', "%{$search}%")
                        ->orWhere('project_description', 'like', "%{$search}%");
                });
            }

            // Apply status filter
            if ($status) {
                $query->where('status', $status);
            }

            // Apply priority filter
            if ($priority) {
                $query->where('priority', $priority);
            }

            // Apply team leader filter
            if ($teamLeader) {
                $query->where('team_leader', $teamLeader);
            }

            // Apply date range filter
            if ($startDateFrom) {
                $query->whereDate('start_date', '>=', $startDateFrom);
            }
            if ($startDateTo) {
                $query->whereDate('start_date', '<=', $startDateTo);
            }

            // Apply sorting
            $query->orderBy($sortBy, $sortOrder);

            // Get paginated results
            $projects = $query->paginate(15);

            // Format the projects data using the formatProjectResponse method
            $formattedProjects = $projects->getCollection()->map(function ($project) {
                return $this->formatProjectResponse($project);
            });

            // Create modern pagination response
            $paginationData = [
                'data' => $formattedProjects,
                'current_page' => $projects->currentPage(),
                'next_page' => $projects->hasMorePages()
                    ? $projects->currentPage() + 1
                    : null,
                'last_page' => $projects->lastPage(),
                'total_items' => $projects->total(),
                'per_page' => $projects->perPage(),
                'filters' => [
                    'search' => $search,
                    'status' => $status,
                    'priority' => $priority,
                    'team_leader' => $teamLeader,
                    'start_date_from' => $startDateFrom,
                    'start_date_to' => $startDateTo,
                    'sort_by' => $sortBy,
                    'sort_order' => $sortOrder,
                ]
            ];

            return $this->success('Projects retrieved successfully', $paginationData);

        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function delete_project_media_file(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'media_file_id' => ['required', 'exists:project_media_files,id'],
        ]);

        $mediaFile = ProjectMediaFile::findOrFail($request->media_file_id);
        $project = $mediaFile->project;

        $hasOverride = $user->can('project_management.project.override');

        // Check if user is team leader or team member
        $isTeamLeader = $user->id === $project->team_leader;
        $isTeamMember = $project->project_assignees()->where('user_id', $user->id)->exists();

        if (!$hasOverride && !$isTeamLeader && !$isTeamMember) {
            return $this->error('Forbidden', 403);
        }

        try {
            DB::beginTransaction();

            // Store file info for activity log before deletion
            $fileName = $mediaFile->file_name;
            $filePath = $mediaFile->file_path;

            // Delete the physical file from storage
            if (Storage::disk('public')->exists($filePath)) {
                Storage::disk('public')->delete($filePath);
            }

            // Delete the database record (soft delete)
            $mediaFile->update([
                'is_deleted' => true,
                'deleted_at' => now(),
            ]);

            // Log Activity for MEDIA FILE deletion
            activity('project_management')
                ->performedOn($project)
                ->causedBy($user)
                ->withProperties([
                    'project_name' => $project->project_name,
                    'file_name' => $fileName,
                    'file_path' => $filePath,
                    'ip' => $request->ip(),
                    'user_agent' => $request->userAgent(),
                ])
                ->log("Media file '{$fileName}' deleted from project '{$project->project_name}' by {$user->name}.");

            DB::commit();

            $this->sendProjectMediaNotifications($project, $mediaFile, $user, 'deleted');

            return $this->success('Media file deleted successfully', [
                'id' => $mediaFile->id,
                'file_name' => $fileName,
                'deleted_at' => $mediaFile->deleted_at,
            ]);

        } catch (Throwable $th) {
            DB::rollBack();
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_project_media_files(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
        ]);

        $project = Project::findOrFail($request->project_id);

        $hasRead = $user->can('project_management.project.read');
        $hasOverride = $user->can('project_management.project.override');
        $isTeamLeader = $user->id === $project->team_leader;
        $isTeamMember = $project->project_assignees()->where('user_id', $user->id)->exists();

        if (!$hasRead && !$hasOverride && !$isTeamLeader && !$isTeamMember) {
            return $this->error('Forbidden', 403);
        }

        try {
            $paginatedMediaFiles = $project->project_media_file()
                ->where('is_deleted', 0)
                ->with('creator:id,name,email,image_path')
                ->orderBy('created_at', 'desc')
                ->paginate(15);

            $mediaFiles = $paginatedMediaFiles->getCollection()->map(function ($mediaFile) {
                return [
                    'id' => $mediaFile->id,
                    'file_name' => $mediaFile->file_name,
                    'file_path' => Storage::disk('public')->url($mediaFile->file_path),
                    'file_type' => $mediaFile->file_type,
                    'file_size' => $mediaFile->file_size,
                    'created_at' => $mediaFile->created_at,
                    'updated_at' => $mediaFile->updated_at,
                    'creator' => $mediaFile->creator ? [
                        'id' => $mediaFile->creator->id,
                        'name' => $mediaFile->creator->name,
                        'email' => $mediaFile->creator->email,
                        'image_path' => $mediaFile->creator->image_path ? Storage::disk('public')->url($mediaFile->creator->image_path) : null,
                    ] : null,
                ];
            });

            return $this->success('Media files retrieved successfully', [
                'data' => $mediaFiles,
                'current_page' => $paginatedMediaFiles->currentPage(),
                'next_page' => $paginatedMediaFiles->hasMorePages()
                    ? $paginatedMediaFiles->currentPage() + 1
                    : null,
                'last_page' => $paginatedMediaFiles->lastPage(),
                'total_items' => $paginatedMediaFiles->total(),
                'per_page' => $paginatedMediaFiles->perPage(),
            ]);
        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function fetch_project_tickets(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
        ]);

        $project = Project::findOrFail($request->project_id);

        $hasRead = $user->can('project_management.project.read');
        $hasOverride = $user->can('project_management.project.override');
        $isTeamLeader = $user->id === $project->team_leader;
        $isTeamMember = $project->project_assignees()->where('user_id', $user->id)->exists();

        if (!$hasRead && !$hasOverride && !$isTeamLeader && !$isTeamMember) {
            return $this->error('Forbidden', 403);
        }

        try {
            $paginatedTickets = $project->project_ticket()
                ->where('is_deleted', 0)
                ->with('creator:id,name,email,image_path')
                ->orderBy('created_at', 'desc')
                ->paginate(15);

            $tickets = $paginatedTickets->getCollection()->map(function ($ticket) {
                return $this->formatTicketResponse($ticket);
            });

            return $this->success('Tickets retrieved successfully', [
                'data' => $tickets,
                'current_page' => $paginatedTickets->currentPage(),
                'next_page' => $paginatedTickets->hasMorePages()
                    ? $paginatedTickets->currentPage() + 1
                    : null,
                'last_page' => $paginatedTickets->lastPage(),
                'total_items' => $paginatedTickets->total(),
                'per_page' => $paginatedTickets->perPage(),
            ]);
        } catch (Throwable $th) {
            return $this->error($th->getMessage(), 500);
        }
    }

    public function create_project_ticket(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'project_id' => ['required', 'exists:projects,id'],
            'name' => ['required', 'string', 'max:255'],
            'description' => ['required', 'string'],
            'priority' => ['required', 'string', 'in:low,medium,high'],
        ]);

        $project = Project::findOrFail($request->project_id);

        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && $user->id !== $project->team_leader) {
            return $this->error('Forbidden', 403);
        }

        $ticket = $project->project_ticket()->create([
            'project_id' => $request->project_id,
            'created_by' => $user->id,
            'name' => $request->name,
            'description' => $request->description,
            'priority' => $request->priority,
            'created_at' => now()
        ]);

        activity('project_management')
            ->performedOn($project)
            ->causedBy($user)
            ->withProperties([
                'project_name' => $project->project_name,
                'ticket_name' => $ticket->name,
                'ticket_priority' => $ticket->priority,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
            ])
            ->log("Project ticket '{$ticket->name}' created for project '{$project->project_name}' by {$user->name}.");

        $this->sendProjectTicketNotifications($project, $ticket, $user, 'created');

        $formatted_data = $this->formatTicketResponse($project->fresh());

        return $this->success('Ticket created successfully', $formatted_data);
    }

    public function update_project_ticket(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'ticket_id' => ['required', 'exists:project_tickets,id'],
            'name' => ['required', 'string', 'max:255'],
            'description' => ['required', 'string'],
            'priority' => ['required', 'string', 'in:low,medium,high'],
            'status' => ['required', 'string', 'in:open,closed']
        ]);

        $ticket = ProjectTicket::findOrFail($request->ticket_id);

        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && $user->id !== $ticket->created_by) {
            return $this->error('Forbidden', 403);
        }

        $ticket->update([
            'name' => $request->name,
            'description' => $request->description,
            'priority' => $request->priority,
            'status' => $request->status,
        ]);

        $project = $ticket->project;

        activity('project_management')
            ->performedOn($project)
            ->causedBy($user)
            ->withProperties([
                'project_name' => $project->project_name,
                'ticket_name' => $ticket->name,
                'ticket_priority' => $ticket->priority,
                'ticket_status' => $ticket->status,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
            ])
            ->log("Project ticket '{$ticket->name}' updated for project '{$project->project_name}' by {$user->name}.");

        $this->sendProjectTicketNotifications($project, $ticket, $user, 'updated');

        return $this->success('Ticket updated successfully', $ticket);
    }

    public function delete_project_ticket(Request $request)
    {
        $user = $request->user();

        $request->validate([
            'ticket_id' => ['required', 'exists:project_tickets,id'],
        ]);

        $ticket = ProjectTicket::findOrFail($request->ticket_id);
        $project = $ticket->project;

        $hasOverride = $user->can('project_management.project.override');

        if (!$hasOverride && $user->id !== $ticket->created_by) {
            return $this->error('Forbidden', 403);
        }

        try {
            DB::beginTransaction();

            $ticket->update([
                'is_deleted' => 1,
                'deleted_at' => now()
            ]);

            activity('project_management')
                ->performedOn($project)
                ->causedBy($user)
                ->withProperties([
                    'project_name' => $project->project_name,
                    'ticket_name' => $ticket->name,
                    'ip' => $request->ip(),
                    'user_agent' => $request->userAgent(),
                ])
                ->log("Project ticket '{$ticket->name}' deleted from project '{$project->project_name}' by {$user->name}.");

            DB::commit();

            $this->sendProjectTicketNotifications($project, $ticket, $user, 'deleted');

            return $this->success('Ticket deleted successfully', [
                'ticket_id' => $request->ticket_id,
                'ticket_name' => $ticket->name,
            ]);
        } catch (Throwable $th) {
            DB::rollBack();
            return $this->error($th->getMessage(), 500);
        }
    }
}
