<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\HrRequestResource;
use App\Models\HrRequest;
use App\Models\HrSetting;
use App\Support\AccessControl;
use App\Support\CrmModuleManager;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class HrRequestController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(HrRequest::class, 'hrRequest');
    }

    public function index(Request $request)
    {
        $search = trim((string) $request->input('q', ''));
        $type = trim((string) $request->input('type', ''));
        $status = trim((string) $request->input('status', ''));
        $priority = trim((string) $request->input('priority', ''));
        $dateFrom = trim((string) $request->input('date_from', ''));
        $dateTo = trim((string) $request->input('date_to', ''));
        $userId = $request->integer('user_id');

        $query = HrRequest::query()
            ->with(['employee:id,name,email', 'assignee:id,name,email', 'creator:id,name,email'])
            ->when($search !== '', function ($builder) use ($search): void {
                $builder->where(function ($sub) use ($search): void {
                    $sub->where('title', 'like', "%{$search}%")
                        ->orWhere('description', 'like', "%{$search}%")
                        ->orWhere('external_id', 'like', "%{$search}%");
                });
            })
            ->when(in_array($type, $this->typeValues(), true), fn ($builder) => $builder->where('type', $type))
            ->when(in_array($status, $this->statusValues(), true), fn ($builder) => $builder->where('status', $status))
            ->when(in_array($priority, $this->priorityValues(), true), fn ($builder) => $builder->where('priority', $priority))
            ->when($dateFrom !== '', fn ($builder) => $builder->whereDate('requested_date', '>=', $dateFrom))
            ->when($dateTo !== '', fn ($builder) => $builder->whereDate('requested_date', '<=', $dateTo));

        if (! AccessControl::isElevated($request->user())) {
            $query->where(function ($sub) use ($request): void {
                $sub->where('employee_user_id', $request->user()->id)
                    ->orWhere('assignee_user_id', $request->user()->id)
                    ->orWhere('creator_user_id', $request->user()->id);
            });
        } elseif ($userId > 0) {
            $query->where(function ($sub) use ($userId): void {
                $sub->where('employee_user_id', $userId)
                    ->orWhere('assignee_user_id', $userId)
                    ->orWhere('creator_user_id', $userId);
            });
        }

        $requests = $query
            ->orderByRaw('due_date is null')
            ->orderBy('due_date')
            ->orderByDesc('created_at')
            ->paginate(30)
            ->withQueryString();

        return HrRequestResource::collection($requests);
    }

    public function store(Request $request, CrmModuleManager $moduleManager): HrRequestResource
    {
        $payload = $this->validatedData($request);
        $actor = $request->user();
        $isElevated = AccessControl::isElevated($actor);

        if (! $isElevated) {
            if (($payload['employee_user_id'] ?? null) !== null && (int) $payload['employee_user_id'] !== (int) $actor->id) {
                abort(403);
            }

            $payload['employee_user_id'] = $actor->id;
            $payload['creator_user_id'] = $actor->id;
            $payload['assignee_user_id'] = null;
        } elseif (! ($payload['creator_user_id'] ?? null)) {
            $payload['creator_user_id'] = $actor->id;
        }

        $settings = HrSetting::query()->first();
        if (! $isElevated && $settings && ! ($settings->allow_employee_requests ?? true)) {
            abort(403);
        }

        if (($payload['assignee_user_id'] ?? null) === null && ($settings?->auto_assign_user_id ?? null)) {
            $payload['assignee_user_id'] = (int) $settings->auto_assign_user_id;
        }

        $payload['requested_date'] = $payload['requested_date'] ?: now()->toDateString();
        if (($payload['due_date'] ?? null) === null && ($settings?->default_sla_days ?? 0) > 0) {
            $payload['due_date'] = now()->addDays((int) $settings->default_sla_days)->toDateString();
        }
        if (($payload['status'] ?? '') === 'resolved' && ($payload['resolved_date'] ?? null) === null) {
            $payload['resolved_date'] = now()->toDateString();
        }

        $payload = $moduleManager->applyPayloadHooks('hr.requests.store', $payload, [
            'hook' => 'hr.requests.store',
            'source' => 'api',
            'user_id' => $actor->id,
        ], array_keys($payload));

        $hrRequest = HrRequest::query()->create($payload);

        return HrRequestResource::make($hrRequest->load(['employee', 'assignee', 'creator']));
    }

    public function show(HrRequest $hrRequest): HrRequestResource
    {
        return HrRequestResource::make($hrRequest->load(['employee', 'assignee', 'creator']));
    }

    public function update(Request $request, HrRequest $hrRequest, CrmModuleManager $moduleManager): HrRequestResource
    {
        $payload = $this->validatedData($request, $hrRequest);
        $actor = $request->user();

        if (! AccessControl::isElevated($actor)) {
            $payload['employee_user_id'] = $hrRequest->employee_user_id;
            $payload['assignee_user_id'] = $hrRequest->assignee_user_id;
            $payload['creator_user_id'] = $hrRequest->creator_user_id ?: $actor->id;
            $payload['external_id'] = $hrRequest->external_id;
        } elseif (! ($payload['creator_user_id'] ?? null)) {
            $payload['creator_user_id'] = $hrRequest->creator_user_id ?: $actor->id;
        }

        if (($payload['status'] ?? '') === 'resolved' && ($payload['resolved_date'] ?? null) === null) {
            $payload['resolved_date'] = now()->toDateString();
        }
        if (($payload['status'] ?? '') !== 'resolved') {
            $payload['resolved_date'] = null;
        }

        $payload = $moduleManager->applyPayloadHooks('hr.requests.update', $payload, [
            'hook' => 'hr.requests.update',
            'source' => 'api',
            'user_id' => $actor->id,
            'hr_request_id' => $hrRequest->id,
        ], array_keys($payload));

        $hrRequest->fill($payload)->save();

        return HrRequestResource::make($hrRequest->fresh()->load(['employee', 'assignee', 'creator']));
    }

    public function destroy(HrRequest $hrRequest)
    {
        $hrRequest->delete();

        return response()->noContent();
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedData(Request $request, ?HrRequest $hrRequest = null): array
    {
        $validated = $request->validate([
            'external_id' => [
                'nullable',
                'string',
                'max:255',
                Rule::unique('hr_requests', 'external_id')->ignore($hrRequest?->id),
            ],
            'type' => ['required', Rule::in($this->typeValues())],
            'status' => ['required', Rule::in($this->statusValues())],
            'priority' => ['required', Rule::in($this->priorityValues())],
            'title' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'requested_date' => ['nullable', 'date'],
            'due_date' => ['nullable', 'date'],
            'resolved_date' => ['nullable', 'date'],
            'employee_user_id' => ['nullable', 'integer', 'exists:users,id'],
            'assignee_user_id' => ['nullable', 'integer', 'exists:users,id'],
            'creator_user_id' => ['nullable', 'integer', 'exists:users,id'],
            'meta' => ['nullable', 'array'],
        ]);

        return [
            'external_id' => $this->nullableText($validated['external_id'] ?? null),
            'type' => (string) $validated['type'],
            'status' => (string) $validated['status'],
            'priority' => (string) $validated['priority'],
            'title' => trim((string) $validated['title']),
            'description' => $this->nullableText($validated['description'] ?? null),
            'requested_date' => $validated['requested_date'] ?? null,
            'due_date' => $validated['due_date'] ?? null,
            'resolved_date' => $validated['resolved_date'] ?? null,
            'employee_user_id' => $validated['employee_user_id'] ?? null,
            'assignee_user_id' => $validated['assignee_user_id'] ?? null,
            'creator_user_id' => $validated['creator_user_id'] ?? null,
            'meta' => is_array($validated['meta'] ?? null) ? $validated['meta'] : null,
        ];
    }

    /**
     * @return list<string>
     */
    private function typeValues(): array
    {
        return ['vacation', 'sick_leave', 'business_trip', 'reimbursement', 'onboarding', 'offboarding', 'other'];
    }

    /**
     * @return list<string>
     */
    private function statusValues(): array
    {
        return ['new', 'in_progress', 'waiting_approval', 'resolved', 'rejected'];
    }

    /**
     * @return list<string>
     */
    private function priorityValues(): array
    {
        return ['low', 'medium', 'high', 'urgent'];
    }

    private function nullableText(mixed $value): ?string
    {
        $value = trim((string) $value);

        return $value === '' ? null : $value;
    }
}
