<?php

namespace App\Http\Traits;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;

/**
 * Handles Exceptions Trait
 *
 * Provides standardized error handling for controllers.
 * Wraps operations in try-catch blocks with proper logging
 * and user-friendly error messages.
 */
trait HandlesExceptions
{
    /**
     * Execute a database operation with transaction and error handling
     *
     * @param callable $callback The operation to execute
     * @param string $successMessage Success message for redirect
     * @param string $errorMessage Error message for redirect
     * @param string|null $redirectRoute Route to redirect to on success (uses back() if null)
     * @return RedirectResponse
     */
    protected function executeWithTransaction(
        callable $callback,
        string $successMessage = 'Operation completed successfully',
        string $errorMessage = 'Operation failed. Please try again.',
        ?string $redirectRoute = null
    ): RedirectResponse {
        try {
            DB::beginTransaction();

            $result = $callback();

            DB::commit();

            $redirect = $redirectRoute
                ? redirect()->route($redirectRoute)
                : redirect()->back();

            return $redirect->with('success', $successMessage);

        } catch (ValidationException $e) {
            DB::rollBack();
            return redirect()->back()
                ->withErrors($e->errors())
                ->withInput();

        } catch (\Exception $e) {
            DB::rollBack();
            $this->logError($e, __METHOD__);

            return redirect()->back()
                ->with('error', $errorMessage)
                ->withInput();
        }
    }

    /**
     * Execute an AJAX operation with error handling
     *
     * @param callable $callback The operation to execute
     * @param string $successMessage Success message for JSON response
     * @param string $errorMessage Error message for JSON response
     * @return JsonResponse
     */
    protected function executeJsonOperation(
        callable $callback,
        string $successMessage = 'Operation completed successfully',
        string $errorMessage = 'Operation failed. Please try again.'
    ): JsonResponse {
        try {
            $result = $callback();

            return response()->json([
                'success' => true,
                'message' => $successMessage,
                'data' => $result,
            ]);

        } catch (ValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $e->errors(),
            ], 422);

        } catch (\Exception $e) {
            $this->logError($e, __METHOD__);

            return response()->json([
                'success' => false,
                'message' => $errorMessage,
            ], 500);
        }
    }

    /**
     * Execute a database transaction with JSON response
     *
     * @param callable $callback The operation to execute
     * @param string $successMessage Success message for JSON response
     * @param string $errorMessage Error message for JSON response
     * @return JsonResponse
     */
    protected function executeJsonWithTransaction(
        callable $callback,
        string $successMessage = 'Operation completed successfully',
        string $errorMessage = 'Operation failed. Please try again.'
    ): JsonResponse {
        try {
            DB::beginTransaction();

            $result = $callback();

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => $successMessage,
                'data' => $result,
            ]);

        } catch (ValidationException $e) {
            DB::rollBack();

            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $e->errors(),
            ], 422);

        } catch (\Exception $e) {
            DB::rollBack();
            $this->logError($e, __METHOD__);

            return response()->json([
                'success' => false,
                'message' => $errorMessage,
            ], 500);
        }
    }

    /**
     * Log an error with context
     *
     * @param \Exception $e The exception to log
     * @param string $context The context/method where error occurred
     * @return void
     */
    protected function logError(\Exception $e, string $context = ''): void
    {
        $logPrefix = method_exists($this, 'getLogPrefix')
            ? $this->getLogPrefix()
            : class_basename(static::class);

        Log::error("[{$logPrefix}] Error in {$context}: " . $e->getMessage(), [
            'exception' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
        ]);
    }

    /**
     * Wrap a simple operation with try-catch
     *
     * @param callable $callback The operation to execute
     * @param mixed $default Default value to return on error
     * @return mixed
     */
    protected function tryCatch(callable $callback, mixed $default = null): mixed
    {
        try {
            return $callback();
        } catch (\Exception $e) {
            $this->logError($e, __METHOD__);
            return $default;
        }
    }

    /**
     * Return a standardized error JSON response
     *
     * @param string $message Error message
     * @param int $statusCode HTTP status code
     * @return JsonResponse
     */
    protected function errorJson(string $message, int $statusCode = 500): JsonResponse
    {
        return response()->json([
            'success' => false,
            'message' => $message,
        ], $statusCode);
    }

    /**
     * Return a standardized success JSON response
     *
     * @param string $message Success message
     * @param mixed $data Additional data to include
     * @return JsonResponse
     */
    protected function successJson(string $message, mixed $data = null): JsonResponse
    {
        $response = [
            'success' => true,
            'message' => $message,
        ];

        if ($data !== null) {
            $response['data'] = $data;
        }

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

    /**
     * Return a not found JSON response
     *
     * @param string $resource The resource type that wasn't found
     * @return JsonResponse
     */
    protected function notFoundJson(string $resource = 'Resource'): JsonResponse
    {
        return $this->errorJson("{$resource} not found", 404);
    }

    /**
     * Return an unauthorized JSON response
     *
     * @param string $message Custom unauthorized message
     * @return JsonResponse
     */
    protected function unauthorizedJson(string $message = 'Unauthorized action'): JsonResponse
    {
        return $this->errorJson($message, 403);
    }
}
