<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Api\CiRequestAdapter;
use App\Models\User;
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\Response;

class BaseApiController extends Controller
{
    protected Request $laravelRequest;
    protected CiRequestAdapter $request;
    protected ValidatorContract|null $validator = null;

    public function __construct()
    {
        $this->laravelRequest = request();
        $this->request = new CiRequestAdapter($this->laravelRequest);
    }

    protected function success($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse
    {
        return response()->json([
            'status'  => true,
            'message' => $message,
            'data'    => $data,
        ], $code);
    }

    protected function error(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST, ?array $errors = null): JsonResponse
    {
        $payload = [
            'status'  => false,
            'message' => $message,
        ];
        if (!empty($errors)) {
            $payload['errors'] = $errors;
        }

        return response()->json($payload, $code);
    }

    protected function respondSuccess($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse
    {
        return $this->success($data, $message, $code);
    }

    protected function respondCreated($data = null, string $message = 'Resource created successfully'): JsonResponse
    {
        return $this->success($data, $message, Response::HTTP_CREATED);
    }

    protected function respondDeleted($data = null, string $message = 'Resource deleted successfully'): JsonResponse
    {
        return $this->success($data, $message, Response::HTTP_OK);
    }

    protected function respondError(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST): JsonResponse
    {
        return $this->error($message, $code);
    }

    protected function respondValidationError(array $errors, string $message = 'Validation failed'): JsonResponse
    {
        return $this->error($message, Response::HTTP_UNPROCESSABLE_ENTITY, $errors);
    }

    protected function validateRequest(array $data, array $rules): array
    {
        $normalized = [];
        foreach ($rules as $field => $ruleSet) {
            $normalized[$field] = $this->normalizeRules($ruleSet);
        }

        $validator = Validator::make($data, $normalized);
        $this->validator = $validator;

        if ($validator->fails()) {
            return $validator->errors()->toArray();
        }

        return [];
    }

    protected function validate(array $rules): bool
    {
        $payload = $this->payloadData();
        $errors = $this->validateRequest($payload, $rules);
        return empty($errors);
    }

    protected function payloadData(): array
    {
        $json = json_decode($this->laravelRequest->getContent() ?: '', true);
        if (is_array($json)) {
            return $json;
        }
        return array_merge($this->laravelRequest->query->all(), $this->laravelRequest->request->all());
    }

    protected function normalizeRules(array|string $ruleSet): array|string
    {
        if (is_array($ruleSet)) {
            return $ruleSet;
        }
        $parts = explode('|', $ruleSet);
        $result = [];
        foreach ($parts as $part) {
            $part = trim($part);
            if ($part === '') {
                continue;
            }
            if (preg_match('/^max_length\\[(\\d+)\\]$/', $part, $m)) {
                $result[] = 'max:' . $m[1];
                continue;
            }
            if (preg_match('/^min_length\\[(\\d+)\\]$/', $part, $m)) {
                $result[] = 'min:' . $m[1];
                continue;
            }
            if (preg_match('/^in_list\\[(.+)\\]$/', $part, $m)) {
                $result[] = 'in:' . $m[1];
                continue;
            }
            if (preg_match('/^greater_than_equal_to\\[(.+)\\]$/', $part, $m)) {
                $result[] = 'gte:' . $m[1];
                continue;
            }
            if (preg_match('/^less_than_equal_to\\[(.+)\\]$/', $part, $m)) {
                $result[] = 'lte:' . $m[1];
                continue;
            }
            if (preg_match('/^valid_date\\[(.+)\\]$/', $part, $m)) {
                $result[] = 'date_format:' . $m[1];
                continue;
            }
            if ($part === 'valid_email') {
                $result[] = 'email';
                continue;
            }
            if ($part === 'permit_empty') {
                $result[] = 'nullable';
                continue;
            }
            if (preg_match('/^is_unique\\[([^\\.]+)\\.([^\\]]+)\\]$/', $part, $m)) {
                $result[] = 'unique:' . $m[1] . ',' . $m[2];
                continue;
            }
            if ($part === 'string') {
                $result[] = 'string';
                continue;
            }
            if ($part === 'integer') {
                $result[] = 'integer';
                continue;
            }
            if ($part === 'numeric') {
                $result[] = 'numeric';
                continue;
            }
            if ($part === 'required') {
                $result[] = 'required';
                continue;
            }
            if ($part === 'alpha_numeric') {
                $result[] = 'alpha_num';
                continue;
            }
            if ($part === 'in_array') {
                $result[] = 'in_array';
                continue;
            }
            if ($part === 'valid_url') {
                $result[] = 'url';
                continue;
            }
            if (strpos($part, 'max_size') === 0) {
                $result[] = str_replace('max_size', 'max', $part);
                continue;
            }
            $result[] = $part;
        }

        return $result;
    }

    protected function paginate($source, int $page = 1, int $perPage = 20): array
    {
        $page    = max(1, $page);
        $perPage = max(1, $perPage);
        $offset  = ($page - 1) * $perPage;

        if ($source instanceof EloquentBuilder || $source instanceof \Illuminate\Database\Query\Builder) {
            $total = $source->toBase()->getCountForPagination();
            $items = $source->offset($offset)->limit($perPage)->get()->toArray();
        } elseif (is_array($source)) {
            $total = count($source);
            $items = array_slice($source, $offset, $perPage);
        } else {
            return [
                'data' => [],
                'pagination' => [
                    'current_page' => $page,
                    'per_page'     => $perPage,
                    'total'        => 0,
                    'total_pages'  => 0,
                ],
            ];
        }

        return [
            'data' => array_values($items),
            'pagination' => [
                'current_page' => $page,
                'per_page'     => $perPage,
                'total'        => $total,
                'total_pages'  => (int) ceil($total / $perPage),
            ],
        ];
    }

    protected function getCurrentUserId(): ?int
    {
        $userId = (int) (session()->get('user_id') ?? 0);
        return $userId > 0 ? $userId : null;
    }

    protected function getCurrentUser(): ?object
    {
        $userId = $this->getCurrentUserId();
        if (!$userId) {
            return null;
        }

        $user = app(User::class);
        $row = $user->find($userId);
        if (!$row) {
            return null;
        }

        $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? ''));
        if ($name === '') {
            $name = $row['email'] ?? 'User #' . $userId;
        }

        $roles = (array) (session()->get('roles') ?? []);
        if (empty($roles)) {
            try {
                $roles = DB::table('user_roles ur')
                    ->select('LOWER(r.name) AS name')
                    ->join('roles r', 'r.id', '=', 'ur.role_id')
                    ->where('ur.user_id', $userId)
                    ->whereNull('ur.deleted_at')
                    ->pluck('name')
                    ->map(fn($name) => strtolower((string) $name))
                    ->unique()
                    ->values()
                    ->toArray();
            } catch (\Throwable $e) {
                Log::error('Unable to load user roles: ' . $e->getMessage());
                $roles = [];
            }
        }

        return (object) [
            'id'    => (int) $userId,
            'name'  => $name,
            'email' => $row['email'] ?? null,
            'roles' => $roles,
        ];
    }
}
