<?php

namespace App\Modules\Bookings;

use Illuminate\Support\Facades\App;
use App\Exceptions\JobModifiedSinceLastOpenedException;
use App\Modules\Clients\Client;
use App\Modules\Clients\Customer;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Exception;
use Illuminate\Http\Request;
use App\Models\KFInvoice;
use App\Models\KFInvoiceLine;
use App\Models\Status;
use Illuminate\Support\Facades\Response;
use App\Http\Controllers\BaseController;
use Carbon\Carbon;
use App\Models\User;

class TaskControllerREST extends BaseController
{
    /**
     * @var \App\Modules\Bookings\TaskRepositoryInterface
     */
    private $repo;

    /**
     * Constructor.
     *
     * @param \App\Modules\Bookings\TaskRepositoryInterface $repo
     */
    public function __construct(TaskRepositoryInterface $repo)
    {
        $this->repo = $repo;
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index(Request $request)
    {
        $requestUri = md5($request->getRequestUri());

        // Skip cache for real-time data (unassigned queue, subscribed tasks)
        $skipCache = in_array($request->input('results'), ['unassigned', 'subscribed', 'own']);

        // Get the results from the cache if they already exist
        if (!$skipCache && Cache::has($requestUri)) {
            return Cache::get($requestUri);
        }

        if ($request->input('show') == 'qa') {
            return $this->paginate($this->repo->getOpenQATasks());
        }

        if ($request->input('needs_calling')) {
            return $this->paginate($this->repo->getNeedsCallTasks());
        }

        if ($request->input('show') == 'collections') {
            return $this->paginate($this->repo->getCollectionsQueue());
        }

        if ($request->input('show') == 'deliveries') {
            return $this->paginate($this->repo->getDeliveriesQueue());
        }

        $query = Task::query()->select('tasks.*');
        $with = ['status'];

        // Always load severity for sorting and display purposes
        if ($request->input('results') === 'unassigned') {
            $with[] = 'severity';
        }

        if (Auth::check() && !Auth::user()->hasPermission('read_followup_tasks')) {
            $query = $query->where('type', '!=', 'followup');
        }

        if ($request->input('name')) {
            $query = $query->where('name', 'like', $request->input('name') . '%');
        }

        // for /tasks page radios - nadeem
        if ($request->input('myPaid')) {
            switch ($request->input('myPaid')) {
                case 0:
                    $query = $query->myPaidAndUnpaid();
                    break;
                case 1:
                    $query = $query->myPaid();
                    break;
                case 2:
                    $query = $query->myUnpaid();
                    break;
            }
        }

        // for /tasks page radios - nadeem
        if ($request->input('myCompleted')) {
            switch ($request->input('myCompleted')) {
                case 0:
                    $query = $query->myCompletedAndOpen();
                    break;
                case 1:
                    $query = $query->myCompleted();
                    break;
                case 2:
                    $query = $query->myOpen();
                    break;
            }
        }

        if ($request->input('clientId')) {
            $query = $query->where('client_id', $request->input('clientId'))->where('client_type', 'client');
        }

        if ($request->input('customerId')) {
            $query = $query->where('client_id', $request->input('customerId'))->where('client_type', 'customer');
        }

        if ($request->input('invoiced')) {
            $query = $query->where('invoiced', $request->input('invoiced'));
        }

        if ($request->input('subtype')) {
            $query = $query->where('subtype_id', $request->input('subtype'));
        }

        if ($request->input('subtype_id')) {
            $query = $query->where('subtype_id', $request->input('subtype_id'));
        }

        if ($request->input('status')) {
            $query = $query->where('status_id', $request->input('status'));
        }

        if ($request->input('severities')) {
            $with[] = 'severity';
        }

        if ($request->input('severity')) {
            $query = $query->where('severity_id', $request->input('severity'));
        }

        // added by nadeem and junaid - c. 20-28.2.17
        if ($request->input('textSearch')) {
            if (is_numeric($request->input('textSearch'))) {
                $query = $query->where(function ($query) use ($request) {
                    $query->where('tasks.id', 'like', $request->input('textSearch') . '%');
                });
            } else {
                $query = $query->where(function ($query) use ($request) {
                    $query->where('title', 'like', '%' . $request->input('textSearch') . '%')->orWhere('problem', 'like', '%' . $request->input('textSearch') . '%');
                });
            }
        }

        if ($request->input('search')) {
            $query->where('tasks.id', 'like', $request->input('search') . '%');
        }

        if ($request->input('items')) {
            $with[] = 'items';
        }

        if ($request->input('qa')) {
            $with[] = 'qa_tasks';
        }

        if ($request->input('user')) {
            $with[] = 'user';
        }

        if ($request->input('escalated')) {
            $query->where('subtype_id', 7);
            $query->where('completed', 0);
            $query->where('status_id', '!=', 9);
        }

        if ($request->input('prebook', 0)) {
            $query->where('prebook', 1);
        }

        if ($request->input('client_id') && $request->input('client_type')) {
            $query = $query->where('client_id', $request->input('client_id'))->where('client_type', $request->input('client_type'));
        }

        if ($request->input('project_free')) {
            $query->whereNull('project_id');
        }

        if ($request->input('modifiedSince')) {
            $query->where('tasks.updated_at', '>=', $request->input('modifiedSince'));
        }

        if ($request->input('previouslyAssigned') && $request->input('previouslyAssigned') != 'false') {
            $query->leftJoin('assigned_changes', function ($q) use ($request) {
                $q->on('assigned_changes.related_id', '=', 'tasks.id')->on('assigned_changes.related_type', '=', DB::raw("'task'"))->on('assigned_changes.old_user', '=', DB::raw($request->input('assigned_to', getAuthUserId())));
            });
        }

        if ($request->input('show') && $request->input('results') != 'managers') {
            switch ($request->input('show')) {
                case 'completed':
                    $query->completed();
                    break;
                case 'open':
                    $query->open();
                    break;
                case 'accounts':
                    $query->completed()->unpaid()->whereIn('type', ['repair', 'support', 'onsite']);
                    break;
                case 'delegated':
                    $query->where('created_by', $request->input('user_id', Auth::user()->id))->where('assigned_to', '!=', $request->input('user_id', Auth::user()->id));
                    break;
            }
        }

        if ($request->input('type')) {
            $query->where('type', $request->input('type'));
        }

        if ($request->input('client_type') && !$request->input('client_id')) {
            $query->where('client_type', $request->input('client_type'));
        }

        if ($request->input('overdue') && $request->input('overdue') != 'false') {
            $query->overdue();
        }

        if ($request->input('assigned_to')) {
            $query->where('assigned_to', $request->input('assigned_to'));
        }

        if ($request->input('unpaid')) {
            $query->unpaid();
        }

        if ($request->input('paid')) {
            $query->paid();
        }

        if ($request->input('notInvoiced') && $request->input('notInvoiced') != 'false') {
            $query->notInvoiced();
        }

        if ($request->input('invoiced') && $request->input('invoiced') != 'false') {
            $query->where('invoiced', 1);
        }

        if ($request->input('parts_total')) {
            $with[] = 'parts';
        }

        if ($request->input('creator')) {
            $with[] = 'creator';
        }

        if ($request->input('start_date') && $request->input('end_date')) {
            $query->thisMonth($request->input('start_date'), $request->input('end_date'));
        }

        $query->with($with);

        if ($request->input('results')) {
            switch ($request->input('results')) {
                case 'managers':
                    $query->where('completed', '=', 0)->with('user', 'status');
                    break;
                case 'own':
                    $query = $query->where('assigned_to', '=', $request->input('user_id', getAuthUserId()))
                        ->where('status_id', '!=', Status::TASKS_QUALITY_CONTROL)
                        ->open();
                    break;
                case 'unassigned':
                    $query = $query->whereNull('assigned_to')
                        ->where('type', '!=', 'qa')
                        ->where('type', '!=', 'follow')
                        ->open();
                    break;
                case 'subscribed':
                    $query->open()->whereHas('subscribers', function ($q) use ($request) {
                        $q->where('user_id', $request->input('user_id', getAuthUserId()));
                    });
                    break;
            }
        }

        // Sort results based on priority
        $query->join('severities', 'severities.id', '=', 'tasks.severity_id', 'left');

        // Get the array of fillable attributes, that can be ordered by as well
        $sortable = array_merge(with(new Task())->getFillable(), ['completed_at', 'id','created_at']);

        if ($request->input('orderBy') && array_search($request->input('orderBy'), $sortable) !== false) {
            $query->orderBy($request->input('orderBy'), $request->input('orderType', 'asc'));
        } elseif ($request->input('summary')) {
            $query->orderBy('tasks.id', 'desc');
        } else {
            $query->orderBy('severities.priority', 'desc')
                ->orderBy('tasks.due_date', 'asc')
                ->orderBy('tasks.id', 'asc');
        }

        // Apply limits and execute the query
        // $query = $query->take(Input::get('limit', 20))->skip(Input::get('skip', 0))->get();
        // if(Input::get('pagesize'))
        //{
        //   $query = $query->paginate(Input::get('pagesize'));
        // }
        //else {
        $query = $query->paginate($request->input('limit', 50));
        //}
        // Load all the clients
        // unfortuately eager loading is not available
        // for polymorphic relationships at the moment..
        if ($request->input('client') && $request->input('client') != 'false') {
            $clients = Client::query()->with('contract')->whereIn('id', $query->where('client_type', 'client')->pluck('client_id'))->get();
            $customers = Customer::query()->whereIn('id', $query->where('client_type', 'customer')->pluck('client_id'))->get();

            foreach ($query as &$task) {
                if ($task->client_type == 'client') {
                    $task->client = $clients->first(function ($client, $key) use ($task) {
                        return $client->id == $task->client_id;
                    });
                } else {
                    $task->client = $customers->first(function ($client, $key) use ($task) {
                        return $client->id == $task->client_id;
                    });
                }
            }
        }

        foreach ($query as $key => $value) {

            if ($request->input('parts_total')) {
                $value->parts_total = $value->parts_total;
            }

            // Load all the comment stats
            $value->comments_count = [
                'total'   => $value->comments()->count(),
                'unread'  => $value->comments()->unread()->count(),
            ];
        }

        // Sanitize data to remove control characters that cause JSON encoding errors
        $data = $query->getCollection()->toArray();
        array_walk_recursive($data, function(&$item) {
            if (is_string($item)) {
                // Remove control characters except newline and tab
                $item = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $item);
            }
        });

        $response = [
            'success'      => true,
            'data'         => $data,
            'total'        => $query->total(),
            'current_page' => $query->currentPage(),
            'last_page'    => $query->lastPage(),
            'per_page'     => $query->perPage(),
            'from'         => $query->firstItem(),
            'to'           => $query->lastItem(),
        ];

        // Cache the results of this query for 24 hours (except real-time data)
        if (!$skipCache) {
            Cache::put($requestUri, $response, 24 * 60);
        }

        $jsonResponse = response()->json($response, ($response['success']) ? 200 : 400);

        // Add no-cache headers for real-time data
        if ($skipCache) {
            $jsonResponse->header('Cache-Control', 'no-cache, no-store, must-revalidate')
                ->header('Pragma', 'no-cache')
                ->header('Expires', '0');
        }

        return $jsonResponse;
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return Response
     */
    public function store(Request $request)
    {
        // Assign a default status
        $status = Status::query()->repairs()->first();

        if (!$status) {
            return response()->json(['success' => false, 'errors' => ['No statuses defined']], 400);
        }

        $data = $request->except('id');
        $data['status_id'] = $status->id;
        if (isset($data['client_id']) && $data['client_type'] == "client" && isset($data['client_id']) && !isset($data['site_id'])) {
            //If new task came with a cleint but no nsite, find a site.
            $cdetails = Client::query()->findOrFail($data['client_id']);
            $site_details = $cdetails->sites->first();
            //$site_user = $site_details->site_users->first();
            $data['site_id'] = $site_details->id;
            //$data['site_user_id'] = $site_user->id;
        }

        $task = Task::create($data);
        if (!$task->id) {
            return response()->json($task->errors->all(), 400);
        } else {
            return $task->load('status', 'user');
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function show($id, Request $request)
    {
        // Removed caching to ensure users always get real-time data

        // OPTIMIZED VERSION - Reduce from 56 queries to ~5-8 queries while maintaining Laravel5 business logic
        
        // Step 1: First, get just the task with client_type to determine conditional loading
        $taskType = Task::select('id', 'client_type')->findOrFail($id);
        
        // Step 2: Build base relationships array - ONLY use relationships that exist in Laravel5
        $baseRelationships = [
            'user', 
            'client', 
            'parts', 
            'builds', 
            'parts.stock', 
            'customer_notifications', 
            'customer_notifications.creator',  
            'parts.stock.order', 
            'parts.stock.order.status', 
            'creator', 
            'modifier', 
            'standard_price', 
            'status', 
            'related_tasks', 
            'qa_tasks', 
            'subscribers', 
            'devices.model_number.manufacturer', 
            'templates.items', 
            'template_items', 
            'booking_fee'
        ];

        // Step 3: Add conditional relationships for client type to prevent unnecessary queries
        $conditionalRelationships = [];
        if ($taskType->client_type == 'client') {
            $conditionalRelationships = [
                'client.sites.users', // Deep eager loading for business clients
                'client.contract', 
                'site', 
                'site_user', 
                'passwords'
            ];
        }

        // Step 4: Merge all relationships and load the task with ALL data in ONE optimized query
        $allRelationships = array_merge($baseRelationships, $conditionalRelationships);
        
        $task = Task::with($allRelationships)
            ->with(['revisionHistory' => function ($q) {
                // Optimize: Use whereIn instead of OR condition for better performance
                $q->whereIn('key', ['cancelled_reason', 'no_price_reason'])
                  ->select(['id', 'revisionable_id', 'revisionable_type', 'key', 'old_value', 'new_value', 'created_at'])
                  ->orderBy('created_at', 'desc')
                  ->limit(50); // Limit to recent revisions only
            }])
            ->findOrFail($id);

        // EXACT SAME BUSINESS LOGIC AS LARAVEL5 - NO CHANGES!
        // This ensures frontend gets exactly the same data structure
        
        $task->parts_total = $task->parts_total;

        // Get the people who worked on this (exactly as Laravel5) - Query optimized with proper select and join
        $task->workers = User::select('users.*')
            ->join('assigned_changes', function ($join) {
                $join->on('users.id', '=', 'assigned_changes.old_user')
                     ->orOn('users.id', '=', 'assigned_changes.new_user');
            })
            ->where('related_id', $id)
            ->where('related_type', 'task')
            ->groupBy('users.id')
            ->get()
            ->toArray();

        // All relationships are already loaded via eager loading above - no additional load() calls needed!

        // Sanitize data to remove control characters that cause JSON encoding errors
        $taskArray = $task->toArray();
        array_walk_recursive($taskArray, function(&$item) {
            if (is_string($item)) {
                // Remove control characters except newline and tab
                $item = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $item);
            }
        });

        return $taskArray;
    }



    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function update(\App\Http\Requests\StoreTaskRequest $request, $id)
    {
        $task = Task::findOrFail($id);

        // Check to see if the job has been updated since the user first loaded it
        // and if it has throw an exception
        // We do not want people to overwrite other's changes
        if ($request->has('updated_at') && $request->input('updated_at')) {
            $requestTimestamp = $request->input('updated_at');
            $taskTimestamp = $task->updated_at->toDateTimeString();
            
            // Try to parse both timestamps as Carbon instances for more robust comparison
            try {
                $requestCarbon = \Carbon\Carbon::parse($requestTimestamp);
                $taskCarbon = $task->updated_at;
                
                // Compare with 1 second tolerance to handle minor timing differences
                if (abs($requestCarbon->diffInSeconds($taskCarbon)) > 1) {
                    throw new JobModifiedSinceLastOpenedException();
                }
            } catch (\Exception $e) {
                // Fallback to string comparison if parsing fails
                if ($requestTimestamp != $taskTimestamp) {
                    throw new JobModifiedSinceLastOpenedException();
                }
            }
        }

        if ($task->type == 'onsite' && $request->has('assigned_to') && $task->assigned_to != $request->input('assigned_to')) {
            $reassigning = true;
        }

        $task->fill($request->all());
        
        // Validate QA status requirements before saving
        if ($task->isDirty('status_id') && $task->status_id == 9) {
            // Check if task is assigned to someone
            if (!$task->assigned_to) {
                return Response::json([
                    'success' => false,
                    'message' => 'You must assign this job to someone before you can complete it.',
                    'errors' => ['assigned_to' => ['This field is required when completing a task.']]
                ], 422);
            }
            
            // Check antivirus state for repair tasks
            if ($task->type == 'repair' && !$task->antivirus_state) {
                return Response::json([
                    'success' => false,
                    'message' => 'You must select the current antivirus status from the dropdown.',
                    'errors' => ['antivirus_state' => ['This field is required for repair tasks when setting status to QA.']]
                ], 422);
            }
        }

        try {
            if ($task->save()) {
                if ((isset($reassigning) || $request->input('exchange_sync')) && $task->exchange_id) {
                    try {
                        $service = \App::make('App\Modules\Bookings\CalendarEventService');
                        $service->update($task);
                    } catch (Exception $e) { }
                }

                if ($request->has('templates')) {
                    $task->templates()->sync(collect($request->input('templates'))->pluck('id'));
                }

                if ($request->has('related_tasks')) {
                    $task->related_tasks()->sync(collect($request->input('related_tasks'))->pluck('id'));
                }

                if ($request->has('subscribers')) {
                    $task->subscribers()->sync(collect($request->input('subscribers'))->pluck('id'));
                }

                if ($request->has('devices')) {
                    $task->devices()->sync(collect($request->input('devices'))->pluck('id'));
                }

                // Load essential relationships needed for the response (matching Laravel5)
                $task->load('user', 'template_items', 'templates.items', 'status', 'related_tasks', 'subscribers');

                if ($task->client_type == 'client') {
                    $task->load('client.sites', 'client.sites.users', 'client.contract', 'site', 'site_user');
                } else {
                    // Customers don't have contracts, only load basic client relationship
                    $task->load('client');
                }

                $task->load(['revisionHistory' => function ($q) {
                    $q->where('key', '=', 'cancelled_reason')->orWhere('key', 'no_price_reason');
                }]);

                // Sanitize data to remove control characters that cause JSON encoding errors
                $taskArray = $task->toArray();
                array_walk_recursive($taskArray, function(&$item) {
                    if (is_string($item)) {
                        // Remove control characters except newline and tab
                        $item = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $item);
                    }
                });

                return $taskArray;
            } else {
                return response()->json(['errors' => $task->errors->all()], 400);
            }
        } catch (Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function destroy($id)
    {
        //
    }

    /**
     * Promote a task to a project.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function promote(Request $request, $id)
    {
        $id = explode(' ', $id)[1];
        // Test to make sure the task is not part of a project already
        $task = Task::query()->findOrFail($id);

        if ($task->project_id) {
            return response()->json(['error' => 'The task is already part of a project'], 400);
        }

        $data = $request->all();
        $data['client_id'] = $task->client_id;
        $data['client_type'] = $task->client_type;
        $data['site_id'] = $task->site_id;
        $data['site_user_id'] = $task->site_user_id;

        $project = Project::create($data);

        if (!$project->id) {
            return response()->json(['errors' => $project->errors->all()], 400);
        }

        $task->project_id = $project->id;
        $task->save();

        return $task;
    }

    /**
     * Marks a template item as completed or not.
     */
    public function updateTemplateItem(Request $request, $id)
    {
        $task = Task::query()->findOrFail($id);

        if ($request->input('done', false)) {
            $task->template_items()->detach($request->input('item_id'));
            $task->template_items()->attach($request->input('item_id'), ['template_id' => $request->input('template_id'), 'value' => true, 'created_by' => Auth::user()->id, 'created_at' => Carbon::now()->toDateTimeString(), 'updated_at' => Carbon::now()->toDateTimeString()]);
        } elseif ($request->input('notRequired', false)) {
            $task->template_items()->detach($request->input('item_id'));
            $task->template_items()->attach($request->input('item_id'), ['template_id' => $request->input('template_id'), 'value' => false, 'created_by' => Auth::user()->id, 'created_at' => Carbon::now()->toDateTimeString(), 'updated_at' => Carbon::now()->toDateTimeString()]);
        } else {
            $task->template_items()->detach($request->input('item_id'));
        }
    }

    /**
     * Generate an invoice and send it to Xero.
     *
     * @param  int  $id
     *
     * @return Response
     */
    public function generateInvoice($id)
    {
        $xeroService = app(\App\Modules\Xero\Services\XeroService::class);

        $task = Task::query()->findOrFail($id);

        if ($task->invoice_number) {
            return response()->json(['error' => 'This task has been invoiced already'], 400);
        }

        try {
            // Create or get contact in Xero
            $xeroContact = $xeroService->createOrGetContact($task->client);
            
            // Store Xero contact ID
            $task->client->xero_id = $xeroContact->ContactID;
            if (!$task->client->save()) {
                return response()->json(['errors' => $task->client->errors->all()], 400);
            }

            // Create invoice in Xero
            $invoice = $xeroService->createInvoice([
                'contact_id' => $xeroContact->ContactID,
                'task_id' => $task->id,
                'client_id' => $task->client_id,
                'client_type' => $task->client_type
            ]);
            // Add line items
            $lines = [];

            // Add labor line
            $description = match($task->type) {
                'repair' => 'Repair with ',
                'support' => 'Remote support with ',
                'onsite' => 'Onsite support with ',
                default => 'Labour with '
            };

            $description .= $task->user->name . ' (REF: ' . $task->id . ' on ' . date('d/m/Y', strtotime($task->created_at)) . ')';

            $lines[] = [
                'Description' => $description,
                'Quantity' => 1,
                'UnitAmount' => $task->labour,
                'TaxType' => ($task->client->type == 'customer') ? 'NONE' : 'OUTPUT',
                'AccountCode' => '200' // Sales account
            ];

            // Add parts
            foreach ($task->parts as $part) {
                $lines[] = [
                    'Description' => $part->stock->name,
                    'Quantity' => $part->quantity,
                    'UnitAmount' => $part->price,
                    'TaxType' => $part->vat > 0 ? 'OUTPUT' : 'NONE',
                    'AccountCode' => '200' // Sales account
                ];
            }

            // Create invoice in Xero
            $xeroInvoice = $xeroService->createInvoice([
                'Type' => 'ACCREC',
                'Contact' => ['ContactID' => $xeroContact->ContactID],
                'DueDate' => date('Y-m-d', time() + 60 * 60 * 24 * 14), // 14 days
                'LineItems' => $lines,
                'Reference' => 'Task #' . $task->id
            ]);

            // Update task with invoice details
            $task->invoice_number = $xeroInvoice->InvoiceNumber;
            $task->xero_invoice_id = $xeroInvoice->InvoiceID;
            $task->invoiced_at = date('Y-m-d H:i:s');
            $task->invoiced = 1;

            if (!$task->save()) {
                return response()->json(['errors' => $task->errors->all()], 400);
            }

            return $task;

        } catch (Exception $e) {
            Log::error('Failed to create Xero invoice: ' . $e->getMessage());
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }

    /**
     * Create a new exchange calendar event for a task.
     *
     * @param  int $id [description]
     *
     * @return Response
     */
    public function createExchangeCalendarEvent($id)
    {
        $task = Task::query()->findOrFail($id);

        $service = App::make('App\Modules\Bookings\CalendarEventService');

        try {
            if ($exchange_id = $service->create($task)) {
                $task->exchange_id = $exchange_id;
                $task->save();

                return $task;
            }
        } catch (Exception $e) {
            Log::error($e);

            return response()->json(['error' => $e->getMessage()], 400);
        }

        return response()->json(['error' => 'Failed to create the exchange calendar event!'], 400);
    }

    /**
     * Return a paginated response to the browser.
     *
     * @param  \Illuminate\Pagination\Paginator|\Illuminate\Pagination\LengthAwarePaginator $paginator
     *
     * @return \Illuminate\Http\Response
     */
    protected function paginate($paginator)
    {
        $response = [
            'success'      => true,
            'data'         => $paginator->getCollection()->toArray(),
            'current_page' => $paginator->currentPage(),
            'per_page'     => $paginator->perPage(),
            'from'         => $paginator->firstItem(),
            'to'           => $paginator->lastItem(),
        ];

        // LengthAwarePaginator has total() and lastPage() methods, Paginator does not
        if ($paginator instanceof \Illuminate\Pagination\LengthAwarePaginator) {
            $response['total'] = $paginator->total();
            $response['last_page'] = $paginator->lastPage();
        } else {
            // For simple pagination, we don't have total count
            $response['total'] = null;
            $response['last_page'] = null;
        }

        return response()->json($response, ($response['success']) ? 200 : 400);
    }

    /**
     * Send the text given as parameter to the client throw sms.
     *
     * @param  int $id
     *
     * @return string text
     */
    public function sendSMS(Request $request, $id)
    {
        $task = Task::query()->findOrFail($id);

        // Use SmsManager to automatically detect and use the enabled SMS provider (TextMagic or Twilio)
        $smsManager = new \App\Services\SmsManager();

        $result = $smsManager->sendTaskSMS(
            $task->id,
            $request->input('send_to'),
            $request->input('message')
        );

        if ($result['success']) {
            // Create a comment to log the SMS (similar to Email Customer)
            $user = Auth::user();
            $comment = new Comment();
            $comment->related_id = $task->id;
            $comment->related_type = 'task';
            $comment->message = $request->input('message');
            $comment->sms_to = $request->input('send_to');
            $comment->created_by = $user->id;
            $comment->public = true;
            $comment->save();

            $notification = CustomerNotification::find($result['notification_id']);
            if ($notification) {
                $notification->load('creator');
                return response()->json(['notification' => $notification], 200);
            }
            return response()->json(['error' => 'Notification created but not found'], 400);
        }

        return response()->json(['error' => 'Error sending the SMS: ' . ($result['error'] ?? 'Unknown error')], 400);
    }

    /**
     * Send the message given through email to the email address given.
     *
     * @param  int $id
     *
     * @return string text
     */
    public function sendEmail(Request $request, $id)
    {
        $task = Task::query()->findOrFail($id);

        // Convert line breaks to <br> tags for HTML email
        $htmlMessage = nl2br($request->input('message'));

        // Use GlobalMailerService (same as Email Customer button)
        $sent = \App\Seraph\Services\GlobalMailerService::send(
            'emails.customer-email',
            ['content' => $htmlMessage],
            function ($mail) use ($request) {
                $mail->to($request->input('send_to'))
                     ->subject($request->input('subject'));
            },
            null, // from_email (use default from SMTP settings)
            null, // from_name (use default from SMTP settings)
            false  // use_cc_bcc = false to include CC/BCC from global SMTP settings
        );

        if ($sent) {
            // Create a comment to log the email (similar to Email Customer button)
            $user = Auth::user();
            $comment = new Comment();
            $comment->related_id = $task->id;
            $comment->related_type = 'task';
            $comment->message = $request->input('message');
            $comment->emailed_to = $request->input('send_to');
            $comment->created_by = $user->id;
            $comment->public = true;
            $comment->save();

            $notification = CustomerNotification::create(
                [
                    'type'    => 'email',
                    'sent_to' => $request->input('send_to'),
                    'message' => $request->input('message'),
                    'subject' => $request->input('subject'),
                    'task_id' => $task->id,
                ]
            );

            if ($notification->exists) {
                $notification->load('creator');

                return response()->json(['notification' => $notification], 200);
            }

            return response()->json(['error' => $notification->errors->all()], 400);
        }

        return response()->json(['error' => 'Error sending the email.'], 400);
    }

    /**
     * Get all the untouched tasks.
     *
     * @return Response
     */
    public function untouched(Request $request)
    {
        $date = $request->input('date', Carbon::now()->subDays(3)->toDateTimeString());

        return $this->repo->getUntouchedTasks($date);
    }

    /**
     * Add a password  in the passwords list.
     *
     * @param  int $id
     *
     * @return string text
     */
    public function addKeychain(Request $request, $id)
    {
        try {
            $task = Task::query()->findOrFail($id);
            $task->passwords()->attach($request->input('keychain_id'));
        } catch (\Exception $e) {
            return response()->json(['error' => 'The password has already been added to this task.'], 400);
        }

        return response()->json('The password has been added to the task!', 200);
    }

    /**
     * This function to fix 2 ajax calls in old function in
     * Bookings.directive -> qaTask
     * function()$scope.changeQAAproval.
     *
     * @param  int $id
     *
     */

    public function updateTaskAndQa(Request $request, $id)
    {
        $qaTask = Task::query()->findOrFail($id);
        $relatedTask = Task::query()->findOrFail($qaTask->qa_id);

        if ($request->has('related_task_updated_at')) {
            $frontendTimestamp = $request->input('related_task_updated_at');
            $dbTimestamp = $relatedTask->updated_at;
            
            // Convert both to Carbon instances for proper timezone-aware comparison
            $frontendCarbon = \Carbon\Carbon::parse($frontendTimestamp);
            $dbCarbon = \Carbon\Carbon::parse($dbTimestamp);
            
            // Compare timestamps (Carbon handles timezone differences automatically)
            if (!$frontendCarbon->equalTo($dbCarbon)) {
                throw new JobModifiedSinceLastOpenedException();
            }
        }

        $tasksToCheck = array('qaTask' => $qaTask, 'relatedTask' => $relatedTask);

        foreach ($tasksToCheck as $key => $task) {
            // Check to see if the job has been updated since the user first loaded it
            // and if it has throw an exception
            // We do not want people to overwrite other's changes

            if ($request->has('passed_qa')) {
                $task->passed_qa = $request->input('passed_qa');
            }

            if ($request->has('needs_calling')) {
                $task->needs_calling = $request->input('needs_calling');
            }

            // QA specific task updates
            if ($key == 'qaTask') {
                if ($request->has('qa_task_status_id')) {
                    $task->status_id = $request->input('qa_task_status_id');
                }
                //  qa_updated_at           - relates to QA task updated
                if ($request->has('qa_updated_at')) {
                    $task->updated_at = $request->input('qa_updated_at');
                }
            }

            // related specific task updates
            if ($key == 'relatedTask') {
                //  related_task_updated_at - obvious
                if ($request->has('related_task_status_id')) {
                    $task->status_id = $request->input('related_task_status_id');
                }
                if ($request->has('related_task_updated_at')) {
                    $task->updated_at = $request->input('related_task_updated_at');
                }
            }

            try {
                if (!$task->save()) {
                    return response()->json(['errors' => $task->errors->all()], 400);
                }
                
                // Task saved successfully - Load only essential relationships needed for the response
                $task->load('user', 'status');
                
                // Load related tasks safely with status (limited fields)
                $task->load(['related_tasks' => function ($query) {
                    $query->select('id', 'title', 'status_id', 'type', 'completed', 'assigned_to')->with('status');
                }]);

                if ($task->client_type == 'client') {
                    $task->load('site', 'site_user');
                }

                if ($key == 'qaTask') {
                    $returnTask = $task;
                }
            } catch (\Exception $e) {
                // Handle specific QA validation error
                if (strpos($e->getMessage(), 'You cannot QA your own jobs') !== false) {
                    return response()->json(['errors' => ['You cannot QA your own jobs!']], 400);
                }
                
                // Handle other exceptions
                return response()->json(['errors' => ['An error occurred while updating the task: ' . $e->getMessage()]], 500);
            }
        }
        //  $returnTask = $tasksToCheck['qaTask'];
        return $returnTask;
    }
}
