<?php

namespace App\Http\Controllers\Api\V1\Website;

use App\Domain\Recruitment\Models\Employer;
use App\Domain\Website\Enums\RequisitionStatus;
use App\Domain\Website\Models\EmployerRequisition;
use App\Domain\Website\Services\RequisitionService;
use App\Http\Controllers\Controller;
use App\Http\Resources\RequisitionResource;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

/** Admin requisition inbox and review workflow (§144F–§144K). */
class RequisitionController extends Controller
{
    public function __construct(private readonly RequisitionService $requisitions) {}

    public function index(Request $request): JsonResponse
    {
        abort_unless($request->user()->can('requisition.view'), 403);

        $requisitions = EmployerRequisition::query()
            ->where('company_id', $request->user()->company_id)
            ->with(['positions:id,employer_requisition_id,job_title,quantity', 'assignee:id,name'])
            ->when($request->filled('search'), function ($query) use ($request) {
                $search = '%'.$request->string('search').'%';
                $query->where(fn ($q) => $q
                    ->where('company_name', 'like', $search)
                    ->orWhere('requisition_no', 'like', $search)
                    ->orWhere('contact_name', 'like', $search));
            })
            ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status')))
            ->when($request->filled('country'), fn ($q) => $q->where('country', strtoupper($request->string('country')->toString())))
            ->when($request->boolean('open_only'), fn ($q) => $q->open())
            ->when($request->boolean('mine'), fn ($q) => $q->where('assigned_to', $request->user()->getKey()))
            ->latest('submitted_at')
            ->paginate($request->integer('per_page', 25));

        return ApiResponse::success(
            RequisitionResource::collection($requisitions),
            meta: [
                'statuses' => array_map(
                    fn (RequisitionStatus $status) => ['value' => $status->value, 'label' => $status->label()],
                    RequisitionStatus::cases(),
                ),
            ],
        );
    }

    public function show(Request $request, EmployerRequisition $employerRequisition): JsonResponse
    {
        abort_unless($request->user()->can('requisition.view'), 403);
        $this->assertSameCompany($request, $employerRequisition);

        $employerRequisition->load([
            'positions.jobDemand:id,uuid,demand_code',
            'employer',
            'assignee:id,name',
            'statusHistories.changedBy:id,name',
        ]);

        // Supporting files are only loaded for a reader allowed to see them;
        // an unloaded relation is omitted from the response entirely.
        if ($request->user()->can('document.view')) {
            $employerRequisition->load('documents.documentType', 'documents.owner');
        }

        return ApiResponse::success(new RequisitionResource($employerRequisition));
    }

    public function changeStatus(Request $request, EmployerRequisition $employerRequisition): JsonResponse
    {
        $this->assertSameCompany($request, $employerRequisition);

        $validated = $request->validate([
            'status' => ['required', Rule::in(RequisitionStatus::values())],
            'reason' => ['nullable', 'string', 'max:1000'],
            'information_requested' => ['nullable', 'string', 'max:2000'],
        ]);

        $target = RequisitionStatus::from($validated['status']);

        // Approving and rejecting are decisions; reviewing is routine handling.
        $ability = in_array($target, [RequisitionStatus::Approved, RequisitionStatus::Rejected], true)
            ? ($target === RequisitionStatus::Approved ? 'requisition.approve' : 'requisition.reject')
            : ($target === RequisitionStatus::InformationRequested ? 'requisition.request_info' : 'requisition.review');

        abort_unless($request->user()->can($ability), 403);

        $requisition = $this->requisitions->changeStatus(
            $employerRequisition,
            $target,
            $validated['reason'] ?? null,
            $request->user(),
            $validated['information_requested'] ?? null,
        );

        return ApiResponse::success(
            new RequisitionResource($requisition->load(['positions', 'statusHistories.changedBy:id,name'])),
            "Requisition marked {$target->label()}.",
        );
    }

    public function assign(Request $request, EmployerRequisition $employerRequisition): JsonResponse
    {
        abort_unless($request->user()->can('requisition.review'), 403);
        $this->assertSameCompany($request, $employerRequisition);

        $validated = $request->validate([
            'assigned_to' => ['nullable', 'uuid', Rule::exists('users', 'uuid')->whereNull('deleted_at')],
            'priority' => ['nullable', Rule::in(['low', 'normal', 'high'])],
        ]);

        $employerRequisition->update([
            'assigned_to' => isset($validated['assigned_to'])
                ? \App\Domain\Identity\Models\User::where('uuid', $validated['assigned_to'])->value('id')
                : null,
            'priority' => $validated['priority'] ?? $employerRequisition->priority,
        ]);

        return ApiResponse::success(
            new RequisitionResource($employerRequisition->fresh()->load(['positions', 'assignee:id,name'])),
            'Requisition assigned.',
        );
    }

    /** §144K — turn an approved requisition into draft job demands. */
    public function convert(Request $request, EmployerRequisition $employerRequisition): JsonResponse
    {
        abort_unless($request->user()->can('requisition.convert'), 403);
        $this->assertSameCompany($request, $employerRequisition);

        $validated = $request->validate([
            'employer_id' => ['required', 'uuid', Rule::exists('employers', 'uuid')->whereNull('deleted_at')],
        ]);

        $employer = Employer::where('uuid', $validated['employer_id'])->firstOrFail();
        abort_unless($request->user()->can('view', $employer), 403);

        $requisition = $this->requisitions->convert($employerRequisition, $employer, $request->user());

        return ApiResponse::success(
            new RequisitionResource($requisition->load(['positions.jobDemand:id,uuid,demand_code', 'employer'])),
            'Converted into draft job demands. Set the agency terms before approving them.',
        );
    }

    private function assertSameCompany(Request $request, EmployerRequisition $requisition): void
    {
        abort_unless($requisition->company_id === $request->user()->company_id, 403);
    }
}
