<?php

namespace App\Domain\Communication\Services;

use App\Domain\Audit\Enums\ActivityCategory;
use App\Domain\Audit\Services\ActivityLogger;
use App\Domain\Communication\Enums\NoteCategory;
use App\Domain\Communication\Enums\NoteVisibility;
use App\Domain\Communication\Models\Note;
use App\Domain\Identity\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class NoteService
{
    public function __construct(private readonly ActivityLogger $activity) {}

    /** @throws ValidationException */
    public function create(
        Model $notable,
        string $body,
        NoteCategory $category,
        NoteVisibility $visibility,
        User $author,
        bool $pinned = false,
    ): Note {
        // Writing a note nobody but a narrower audience can read is usually a
        // mistake; refusing it early avoids notes vanishing from their author.
        $required = $visibility->requiredPermission();

        if ($required !== null && ! $author->can($required)) {
            throw ValidationException::withMessages([
                'visibility' => ['You cannot write a note you would not be able to read.'],
            ]);
        }

        return DB::transaction(function () use ($notable, $body, $category, $visibility, $author, $pinned) {
            $note = Note::create([
                'company_id' => $notable->company_id,
                'notable_type' => $notable->getMorphClass(),
                'notable_id' => $notable->getKey(),
                'category' => $category->value,
                'visibility' => $visibility->value,
                'body' => $body,
                'is_pinned' => $pinned,
                'author_id' => $author->getKey(),
                // Snapshotted so the note keeps its attribution.
                'author_name_snapshot' => $author->name,
            ]);

            $this->activity->log(ActivityCategory::Note, 'note.added', [
                'subject' => $notable,
                'actor' => $author,
                'title' => 'Internal note added',
                'description' => $category->label().' · '.$visibility->label(),
                // The note body is not repeated into the activity log; the note
                // itself carries its own visibility rules.
            ]);

            return $note;
        });
    }

    public function delete(Note $note, User $actor): void
    {
        DB::transaction(function () use ($note, $actor) {
            $notable = $note->notable;
            $note->delete();

            $this->activity->log(ActivityCategory::Note, 'note.deleted', [
                'subject' => $notable,
                'actor' => $actor,
                'title' => 'Internal note removed',
                'description' => 'Written by '.$note->authorName(),
            ]);
        });
    }
}
