<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Contracts\Container\BindingResolutionException;
use ReflectionException;
use Throwable;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;

class Handler extends ExceptionHandler
{
    public function report(Throwable $exception)
    {
        parent::report($exception);
    }

    public function render($request, Throwable $exception)
    {
        // Nếu là 404 vì đường dẫn không khớp route, và đường dẫn có tiền tố thuộc phạm vi app (vd dashboard, campaigns, videos,...),
        // chuyển hướng người dùng đã xác thực về `/dashboard` thay vì trả 404.
        // Handle NotFoundHttpException or any Http exception with 404 status
        $is404 = false;
        if ($exception instanceof NotFoundHttpException) {
            $is404 = true;
        } elseif (method_exists($exception, 'getStatusCode') && (int)$exception->getStatusCode() === 404) {
            $is404 = true;
        }

        if ($is404) {
            try {
                // Nếu request JSON/ API thì trả 404 như bình thường
                if (method_exists($request, 'expectsJson') && $request->expectsJson()) {
                    return response()->json(['message' => 'Not Found'], 404);
                }

                // Xác định host ứng dụng (từ config/app.url) hoặc localhost
                $appUrl = config('app.url');
                $appHost = $appUrl ? parse_url($appUrl, PHP_URL_HOST) : null;
                $reqHost = $request->getHost();

                $isAppHost = false;
                if ($appHost && strcasecmp($reqHost, $appHost) === 0) {
                    $isAppHost = true;
                }
                if (in_array($reqHost, ['127.0.0.1', '::1', 'localhost'], true)) {
                    $isAppHost = true;
                }

                // Nếu user đã đăng nhập (Auth hoặc session fallback) và host khớp app,
                // coi như URL rác của app và redirect về dashboard
                $isAuthed = false;
                try {
                    $isAuthed = \Illuminate\Support\Facades\Auth::check() || ($request->session && $request->session()->has('user_id'));
                } catch (\Throwable $_) {
                    $isAuthed = $request->session && $request->session()->has('user_id');
                }

                // If session cookie exists but session store wasn't loaded (host mismatch), try to detect session file contents (file driver)
                if (!$isAuthed) {
                    try {
                        $sessionCookie = $request->cookies->get(config('session.cookie'));
                        if ($sessionCookie) {
                            $sessPath = storage_path('framework/sessions/' . $sessionCookie);
                            if (file_exists($sessPath)) {
                                $contents = @file_get_contents($sessPath) ?: '';
                                if (strpos($contents, 'user_id') !== false) {
                                    $isAuthed = true;
                                }
                            }
                        }
                    } catch (\Throwable $_) {
                        // ignore
                    }
                }

                // If still not authed, and we're dealing with localhost/127.0.0.1 variants,
                // try a best-effort scan of session files for any 'user_id' entry.
                // This helps local dev where cookies set on 'localhost' are not sent to '127.0.0.1' and vice-versa.
                if (!$isAuthed) {
                    try {
                        $localHosts = ['127.0.0.1', '::1', 'localhost'];
                        if (in_array($reqHost, $localHosts, true) || ($appHost && in_array($appHost, $localHosts, true))) {
                            $sessDir = storage_path('framework/sessions');
                            if (is_dir($sessDir)) {
                                $files = @scandir($sessDir) ?: [];
                                foreach ($files as $file) {
                                    if ($file === '.' || $file === '..') continue;
                                    $path = $sessDir . DIRECTORY_SEPARATOR . $file;
                                    if (!is_file($path)) continue;
                                    $contents = @file_get_contents($path) ?: '';
                                    if (strpos($contents, 'user_id') !== false) {
                                        $isAuthed = true;
                                        break;
                                    }
                                }
                            }
                        }
                    } catch (\Throwable $_) {
                        // ignore
                    }
                }

                // Log debug info to help diagnose why redirect didn't happen
                Log::info('notfound-fallback-check', [
                    'path' => $request->path(),
                    'host' => $reqHost,
                    'appHost' => $appHost,
                    'isAppHost' => $isAppHost,
                    'isAuthed' => $isAuthed,
                    'exception_class' => get_class($exception),
                    'status' => method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : null,
                ]);

                if ($isAuthed && $isAppHost) {
                    return redirect()->route('dashboard');
                }
            } catch (\Throwable $e) {
                Log::warning('Error handling NotFound fallback: ' . $e->getMessage());
            }
        }

        try {
            // Nếu là request JSON và có exception, xử lý đặc biệt để tránh lỗi UTF-8
            if ($request->expectsJson()) {
                return $this->renderJsonException($request, $exception);
            }

            return parent::render($request, $exception);
        } catch (BindingResolutionException | ReflectionException $e) {
            // Nếu binding 'view' bị thiếu thì tránh gọi View facade trong quá trình render
            $body = '<!doctype html><html><head><meta charset="utf-8"><title>Server Error</title></head><body><h1>Server error</h1><p>An internal error occurred.</p></body></html>';
            return new \Illuminate\Http\Response($body, method_exists($exception, 'getStatusCode') ? $exception->getStatusCode() : 500);
        } catch (\Throwable $e) {
            // Fallback khi có lỗi trong quá trình render (ví dụ: UTF-8 encoding)
            if ($request->expectsJson()) {
                return response()->json([
                    'message' => 'Server error occurred',
                    'error' => $e->getMessage()
                ], 500);
            }
            throw $e;
        }
    }

    /**
     * Render exception cho JSON request, xử lý UTF-8 đúng cách
     */
    private function renderJsonException($request, Throwable $exception): \Illuminate\Http\JsonResponse
    {
        $status = match (true) {
            $exception instanceof AuthorizationException => 403,
            method_exists($exception, 'getStatusCode') => $exception->getStatusCode(),
            default => 500,
        };

        // Lấy message và làm sạch
        $message = $exception->getMessage() ?: 'Server error';
        $message = $this->sanitizeBinaryString($message);

        $data = [
            'message' => $message,
        ];

        // Thêm trace chỉ khi debug mode
        if (config('app.debug')) {
            $trace = [];
            foreach ($exception->getTrace() as $i => $frame) {
                if ($i > 10) break; // Giới hạn 10 frames
                $trace[] = [
                    'file' => $frame['file'] ?? null,
                    'line' => $frame['line'] ?? null,
                    'function' => $frame['function'] ?? null,
                ];
            }
            $data['trace'] = $trace;
            $data['exception'] = get_class($exception);
        }

        return response()->json($data, $status);
    }

    /**
     * Làm sạch chuỗi có thể chứa binary data (UUID)
     */
    private function sanitizeBinaryString(?string $text): string
    {
        if ($text === null) return '';

        // Chuyển binary bytes thành hex representation
        $text = preg_replace_callback('/[\x00-\x1F\x7F-\xFF]/', function ($matches) {
            return '[0x' . bin2hex($matches[0]) . ']';
        }, $text);

        // Đảm bảo UTF-8 hợp lệ
        if (!mb_check_encoding($text, 'UTF-8')) {
            $text = iconv('UTF-8', 'UTF-8//IGNORE', $text) ?: '';
        }

        return $text;
    }
}
