<?php

namespace App\Http\Controllers\Traits;

trait JsonResponseTrait
{

    public function success($data, $message = '', $statusCode = 200)
    {
        $preparedData = $this->prepareData($data, $message);
        if (empty($preparedData)) {
            return $this->notFound("Record Not Found", 404);
        }
        $response = [
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode
        ];
        // Check if the data is paginated
        if (is_array($preparedData) && isset($preparedData['items']) && isset($preparedData['pagination'])) {
            $response['data'] = $preparedData['items'];
            $response['pagination'] = $preparedData['pagination'];
        } else {
            $response['data'] = $preparedData;
        }
        return response()->json($response, $statusCode);
    }

    public function successWithNestedPagination($data, $message = '', $statusCode = 200)
    {
        $response = [
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode
        ];
        
        if (is_array($data) && count($data) > 0) {
            // Get the first key (should be the collection to paginate)
            $firstKey = array_key_first($data);
            $firstValue = $data[$firstKey];
            
            // Apply pagination to the first value
            $preparedData = $this->prepareData($firstValue, $message);
            
            if (empty($preparedData)) {
                return $this->notFound("Record Not Found", 404);
            }
            
            // Initialize data object
            $response['data'] = [];
            
            // Check if the first value is paginated
            if (is_array($preparedData) && isset($preparedData['items']) && isset($preparedData['pagination'])) {
                $response['data'][$firstKey] = $preparedData['items'];
                $response['pagination'] = $preparedData['pagination'];
            } else {
                $response['data'][$firstKey] = $preparedData;
            }
            
            // Add other keys to the data object
            foreach ($data as $key => $value) {
                if ($key !== $firstKey) {
                    $response['data'][$key] = $value;
                }
            }
        } else {
            $response['data'] = $data;
        }
        
        return response()->json($response, $statusCode);
    }


    public function errorWithData($data, $message = '', $statusCode = 500)
    {
        $data = $this->prepareData($data, $message);
        if (empty($data)) {
            return $this->notFound("Record Not Found", 404);
        }
        return response()->json([
            'data' => $data,
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    public function withToken($data, $token = '', $message = '', $statusCode = 200, $permissions = [], $settings = [])
    {
        $data = $this->prepareData($data, $message);
        if (empty($data)) {
            return $this->notFound("Record Not Found", 404);
        }
        return response()->json([
            'data' => $data,
            'token' => $token,
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode,
            'permissions' => $permissions,
            'settings' => $settings // ← THIS MUST BE HERE
        ], $statusCode);
    }

    public function withCompanyToken($data, $token = '', $admin = '', $message = '', $statusCode = 200)
    {
        $data = $this->prepareData($data, $message);
        if (empty($data)) {
            return $this->notFound("Record Not Found", 404);
        }
        return response()->json([
            'data' => $data,
            'token' => $token,
            'admin' => $admin,
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    public function withCompanyTokenWithSubscription($data, $token = '', $admin = '',  $subscription = '', $permissions = '', $settings = '', $message = '', $statusCode = 200)
    {
        $data = $this->prepareData($data, $message);
        if (empty($data)) {
            return $this->notFound("Record Not Found", 404);
        }
        return response()->json([
            'data' => $data,
            'token' => $token,
            'admin' => $admin,
            'subscription' => $subscription ?? null,
            'permissions' => $permissions ?? null,
            'settings' => $settings ?? null,
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    public function WithFilter($data, $filter = null, $message = '', $statusCode = 200)
    {
        $preparedData = $this->prepareData($data, $message);
        if (empty($preparedData)) {
            return $this->notFound("Record Not Found", 404);
        }
        $response = [
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode,
            'filter' => $filter
        ];
        // Check if the data is paginated
        if (is_array($preparedData) && isset($preparedData['items']) && isset($preparedData['pagination'])) {
            $response['data'] = $preparedData['items'];
            $response['totalCount'] = $preparedData['pagination']['total'];
            $response['pagination'] = $preparedData['pagination'];
        } else {
            $response['data'] = $preparedData;
            $response['totalCount'] = count($preparedData);
        }
        return response()->json($response, $statusCode);
    }

    public function withCount($data, $message = '', $statusCode = 200)
    {
        $preparedData = $this->prepareData($data, $message);
        if (empty($preparedData)) {
            return $this->notFound("Record Not Found", 404);
        }
        $response = [
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode
        ];
        // Check if the data is paginated
        if (is_array($preparedData) && isset($preparedData['items']) && isset($preparedData['pagination'])) {
            $items = $preparedData['items'];
            // Sort items if possible
            if (!empty($items)) {
                $firstItem = reset($items);
                $canSort = $this->canSortByCreatedAt($firstItem);
                if ($canSort) {
                    usort($items, [$this, 'sortByCreatedAt']);
                }
            }
            $response['data'] = $items;
            $response['totalCount'] = $preparedData['pagination']['total'];
            $response['pagination'] = $preparedData['pagination'];
        } else {
            // Handle non-paginated data
            if (!empty($preparedData)) {
                $firstItem = reset($preparedData);
                $canSort = $this->canSortByCreatedAt($firstItem);
                if ($canSort) {
                    usort($preparedData, [$this, 'sortByCreatedAt']);
                }
            }
            $response['data'] = $preparedData;
            $response['totalCount'] = count($preparedData);
        }
        return response()->json($response, $statusCode);
    }

  

    /**
     * Dedicated function for pagination responses with pre-formatted data
     * 
     * @param \Illuminate\Pagination\LengthAwarePaginator $paginatedData The paginated data with metadata
     * @param \Illuminate\Support\Collection $formattedData The formatted data to return
     * @param string $message Custom message for the response
     * @param int $statusCode HTTP status code
     * @return \Illuminate\Http\JsonResponse
     */
    public function withPaginationFormatted($paginatedData, $formattedData, $message = '', $statusCode = 200)
    {
        // Check if there's no data
        if ($paginatedData->total() === 0 || $formattedData->isEmpty()) {
            return response()->json([
                'message' => 'Record Not Found',
                'statusCode' => 404
                
            ], 404);
        }

        // Generate correct URLs with proper port
        $nextPageUrl = $this->generateCorrectPaginationUrl($paginatedData, $paginatedData->currentPage() + 1);
        $prevPageUrl = $this->generateCorrectPaginationUrl($paginatedData, $paginatedData->currentPage() - 1);
        
        $response = [
            'message' => $message ? $message : "success",
            'statusCode' => $statusCode,
            'data' => $formattedData->toArray(),
            'totalCount' => $paginatedData->total(),
            'pagination' => [
                'total' => $paginatedData->total(),
                'per_page' => $paginatedData->perPage(),
                'current_page' => $paginatedData->currentPage(),
                'last_page' => $paginatedData->lastPage(),
                'from' => $paginatedData->firstItem(),
                'to' => $paginatedData->lastItem(),
                'next_page_url' => $nextPageUrl,
                'prev_page_url' => $prevPageUrl
            ]
        ];

        return response()->json($response, $statusCode);
    }

    /**
     * Generate correct pagination URL with proper port and parameters for webhook
     */
    private function generateCorrectPaginationUrl($paginatedData, $page)
    {
        if ($page < 1 || $page > $paginatedData->lastPage()) {
            return null;
        }

        // Get current request parameters but exclude internal middleware parameters
        $currentParams = request()->query();
        $excludeParams = [
            'webhook_authenticated',
            'webhook_url', 
            'api_key_used',
            'webhook_secret_used'
        ];
        
        // Remove excluded parameters
        foreach ($excludeParams as $param) {
            unset($currentParams[$param]);
        }
        
        $currentParams['page'] = $page;

        // Build URL with correct port
        $baseUrl = request()->getSchemeAndHttpHost();
        $path = request()->path();
        $queryString = http_build_query($currentParams);
        return $baseUrl . '/' . $path . ($queryString ? '?' . $queryString : '');
    }

    

    private function canSortByCreatedAt($item)
    {
        if (!is_array($item) && !is_object($item)) {
            return false;
        }
        if (is_array($item) && !isset($item['created_at'])) {
            return false;
        }
        if (is_object($item) && !property_exists($item, 'created_at') && !isset($item->created_at)) {
            return false;
        }
        return true;
    }

    private function sortByCreatedAt($a, $b)
    {
        $aTime = null;
        $bTime = null;
        if (is_array($a) && isset($a['created_at'])) {
            $aTime = $a['created_at'];
        } elseif (is_object($a) && isset($a->created_at)) {
            $aTime = $a->created_at;
        }
        if (is_array($b) && isset($b['created_at'])) {
            $bTime = $b['created_at'];
        } elseif (is_object($b) && isset($b->created_at)) {
            $bTime = $b->created_at;
        }
        if (!$aTime || !$bTime) {
            return 0;
        }
        return strtotime($bTime) - strtotime($aTime);
    }

    public function error($message = '', $statusCode = 500)
    {
        return response()->json([
            'message' => $message ? $message : "something went wrong",
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    public function notFound($message = '', $statusCode = 404)
    {
        return response()->json([
            'message' => $message ? $message : 'Record Not Found',
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    public function message($message = '', $statusCode = 200)
    {
        return response()->json([
            'message' => $message ? $message : 'Record Not Found',
            'statusCode' => $statusCode, // Add the status key here
        ], $statusCode);
    }

    private function prepareData($data, $message)
    {
        if ($data instanceof \Illuminate\Database\Eloquent\Model) {
            $data = $data->toArray();
        } elseif ($data instanceof \Illuminate\Database\Eloquent\Builder) {
            // Check if pagination is explicitly requested via query parameter
            if (request()->has('per_page') || request()->has('page')) {
                $perPage = request()->get('per_page', 10);
                $data = $data->paginate($perPage);
                if ($data->isEmpty()) {
                    return null;
                }
                // Preserve all current query parameters
                $currentParams = request()->query();
                // Generate URLs with all current parameters
                $nextPageUrl = $data->hasMorePages() ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $data->currentPage() + 1])) : null;
                $prevPageUrl = $data->currentPage() > 1 ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $data->currentPage() - 1])) : null;
                // Return paginated data with metadata
                return [
                    'items' => $data->items(),
                    'pagination' => [
                        'total' => $data->total(),
                        'per_page' => $data->perPage(),
                        'current_page' => $data->currentPage(),
                        'last_page' => $data->lastPage(),
                        'from' => $data->firstItem(),
                        'to' => $data->lastItem(),
                        'next_page_url' => $nextPageUrl,
                        'prev_page_url' => $prevPageUrl
                    ]
                ];
            }
            // Default behavior: return all records without pagination
            $data = $data->get();
            if ($data->isEmpty()) {
                return null;
            }
            return $data->toArray();
        } elseif ($data instanceof \Illuminate\Support\Collection) {
            if ($data->isEmpty()) {
                return null;
            }
            // Check if pagination is explicitly requested via query parameter
            if (request()->has('per_page') || request()->has('page')) {
                // For collections, we'll implement manual pagination
                $page = (int)request()->get('page', 1);
                $perPage = (int)request()->get('per_page', 10);
                $total = $data->count();
                $items = $data->forPage($page, $perPage);
                $lastPage = ceil($total / $perPage);
                // Preserve all current query parameters
                $currentParams = request()->query();
                // Generate URLs with all current parameters
                $nextPageUrl = $page < $lastPage ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $page + 1])) : null;
                
                $prevPageUrl = $page > 1 ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $page - 1])) : null;
                // Calculate pagination metadata
                return [
                    'items' => array_values($items->toArray()),
                    'pagination' => [
                        'total' => $total,
                        'per_page' => $perPage,
                        'current_page' => $page,
                        'last_page' => $lastPage,
                        'from' => ($page - 1) * $perPage + 1,
                        'to' => min($page * $perPage, $total),
                        'next_page_url' => $nextPageUrl,
                        'prev_page_url' => $prevPageUrl
                    ]
                ];
            }
            // Default behavior: return all records without pagination
            return $data->toArray();
        } elseif ($data instanceof \Illuminate\Database\Eloquent\Relations\HasMany) {
            // Check if pagination is explicitly requested via query parameter
            if (request()->has('per_page') || request()->has('page')) {
                // Handle HasMany relationships with pagination
                $perPage = request()->get('per_page', 10);
                $data = $data->paginate($perPage);
                if ($data->isEmpty()) {
                    return null;
                }
                // Preserve all current query parameters
                $currentParams = request()->query();
                // Generate URLs with all current parameters
                $nextPageUrl = $data->hasMorePages() ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $data->currentPage() + 1])) : null;
                
                $prevPageUrl = $data->currentPage() > 1 ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $data->currentPage() - 1])) : null;
                return [
                    'items' => $data->items(),
                    'pagination' => [
                        'total' => $data->total(),
                        'per_page' => $data->perPage(),
                        'current_page' => $data->currentPage(),
                        'last_page' => $data->lastPage(),
                        'from' => $data->firstItem(),
                        'to' => $data->lastItem(),
                        'next_page_url' => $nextPageUrl,
                        'prev_page_url' => $prevPageUrl
                    ]
                ];
            }
            // Default behavior: return all records without pagination
            $data = $data->get();
            if ($data->isEmpty()) {
                return null;
            }
            return $data->toArray();
        } elseif (is_array($data)) {
            // Handle plain PHP arrays
            if (empty($data)) {
                return null;
            }
            
            // Check if this is already a structured response array (has pagination, reports, counts, etc.)
            // If so, return it as-is without processing
            $structuredKeys = ['pagination', 'reports', 'counts', 'total_reports', 'items'];
            $hasStructuredKeys = false;
            foreach ($structuredKeys as $key) {
                if (isset($data[$key]) || array_key_exists($key, $data)) {
                    $hasStructuredKeys = true;
                    break;
                }
            }
            
            // If it's already structured, return as-is
            if ($hasStructuredKeys) {
                return $data;
            }
            
            // Check if pagination is explicitly requested via query parameter
            if (request()->has('per_page') || request()->has('page')) {
                // For arrays, we'll implement manual pagination
                $page = (int)request()->get('page', 1);
                $perPage = (int)request()->get('per_page', 10);
                $total = count($data);
                $lastPage = ceil($total / $perPage);
                // Calculate offset and slice the array
                $offset = ($page - 1) * $perPage;
                $items = array_slice($data, $offset, $perPage);
                // Preserve all current query parameters
                $currentParams = request()->query();
                // Generate URLs with all current parameters
                $nextPageUrl = $page < $lastPage ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $page + 1])) : null;
                $prevPageUrl = $page > 1 ? 
                    request()->fullUrlWithQuery(array_merge($currentParams, ['page' => $page - 1])) : null;
                // Calculate pagination metadata
                return [
                    'items' => array_values($items),
                    'pagination' => [
                        'total' => $total,
                        'per_page' => $perPage,
                        'current_page' => $page,
                        'last_page' => $lastPage,
                        'from' => $offset + 1,
                        'to' => min($offset + $perPage, $total),
                        'next_page_url' => $nextPageUrl,
                        'prev_page_url' => $prevPageUrl
                    ]
                ];
            }
            // Default behavior: return all records without pagination
            return $data;
        }
        return empty($data) ? null : $data;
    }
}
