<?php

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

use App\Domain\Applicants\Models\Applicant;
use App\Domain\Communication\Enums\NoteCategory;
use App\Domain\Communication\Enums\NoteVisibility;
use App\Domain\Communication\Models\Note;
use App\Domain\Communication\Services\NoteService;
use App\Http\Controllers\Controller;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

/** Internal notes on an applicant file (§58). */
class ApplicantNoteController extends Controller
{
    public function __construct(private readonly NoteService $notes) {}

    public function index(Request $request, Applicant $applicant): JsonResponse
    {
        $this->authorize('view', $applicant);

        $notes = Note::query()
            ->where('notable_type', $applicant->getMorphClass())
            ->where('notable_id', $applicant->getKey())
            // Bands the reader is not entitled to are not returned at all.
            ->readableBy($request->user())
            ->orderByDesc('is_pinned')
            ->latest('created_at')
            ->get();

        return ApiResponse::success(
            $notes->map(fn (Note $note) => $this->present($note)),
            meta: [
                'categories' => array_map(
                    fn (NoteCategory $category) => ['value' => $category->value, 'label' => $category->label()],
                    NoteCategory::cases(),
                ),
                'visibilities' => array_values(array_map(
                    fn (NoteVisibility $visibility) => [
                        'value' => $visibility->value,
                        'label' => $visibility->label(),
                    ],
                    // Only bands this user could actually write.
                    array_filter(
                        NoteVisibility::cases(),
                        fn (NoteVisibility $v) => $v->requiredPermission() === null
                            || $request->user()->can($v->requiredPermission()),
                    ),
                )),
            ],
        );
    }

    public function store(Request $request, Applicant $applicant): JsonResponse
    {
        $this->authorize('update', $applicant);

        $validated = $request->validate([
            'body' => ['required', 'string', 'max:5000'],
            'category' => ['nullable', Rule::in(NoteCategory::values())],
            'visibility' => ['nullable', Rule::in(NoteVisibility::values())],
            'is_pinned' => ['sometimes', 'boolean'],
        ]);

        $note = $this->notes->create(
            $applicant,
            $validated['body'],
            NoteCategory::from($validated['category'] ?? NoteCategory::General->value),
            NoteVisibility::from($validated['visibility'] ?? NoteVisibility::Staff->value),
            $request->user(),
            $validated['is_pinned'] ?? false,
        );

        return ApiResponse::success($this->present($note), 'Note added.', status: 201);
    }

    public function destroy(Request $request, Applicant $applicant, string $note): JsonResponse
    {
        $this->authorize('update', $applicant);

        $record = Note::query()
            ->where('uuid', $note)
            ->where('notable_type', $applicant->getMorphClass())
            ->where('notable_id', $applicant->getKey())
            ->readableBy($request->user())
            ->first();

        abort_if($record === null, 404);

        // A note is the author's account of something; only they or a manager
        // may withdraw it.
        abort_unless(
            $record->author_id === $request->user()->getKey() || $request->user()->can('applicant.merge'),
            403,
            'Only the author or a manager can remove this note.',
        );

        $this->notes->delete($record, $request->user());

        return ApiResponse::success(null, 'Note removed.');
    }

    /** @return array<string, mixed> */
    private function present(Note $note): array
    {
        return [
            'id' => $note->uuid,
            'body' => $note->body,
            'category' => $note->category->value,
            'category_label' => $note->category->label(),
            'visibility' => $note->visibility->value,
            'visibility_label' => $note->visibility->label(),
            'is_pinned' => $note->is_pinned,
            'author' => $note->authorName(),
            'created_at' => $note->created_at?->toIso8601String(),
        ];
    }
}
