<?php

namespace App\Services;

use App\Interface\HistoryOwner;
use App\Models\Task;
use App\Models\User\User;
use Illuminate\Database\Eloquent\Model;

class TaskService {
    public function addTask(HistoryService $history, $title, $due_at, $description = null, $target_user_id = null, ?Model $owner = null) {
        $task = Task::create([
            'user_id'     => $target_user_id ?: auth()->id(),
            'assigned_by' => auth()->id(),
            'title'       => $title,
            'description' => $description,
            'due_at'      => $due_at,
        ]);

        // Attach the task to its file (polymorphic owner) before logging so the
        // history entry lands on the file rather than the acting user.
        if ($owner) {
            $task->owner()->associate($owner)->save();
        }

        $this->record($history, $task, 'Task added — ' . $this->taskDetails($task), 'added');

        return $task;
    }

    /**
     * Create a system-generated task — one raised by an automated flow rather
     * than a person (e.g. notifying a case handler that an email landed on a
     * client file). There is no acting user, so the assignor is left null and
     * the solicitor must be supplied explicitly: BelongsToSolicitor only
     * auto-scopes HTTP requests, so a task created from a queued job or webhook
     * would otherwise be saved untenanted (solicitor_id null).
     */
    public function addSystemTask(
        HistoryService $history,
        Model $owner,
        User $assignee,
        string $title,
        $due_at,
        string $description,
        int $solicitorId,
        ?int $contactMessageId = null,
    ): Task {
        $task = new Task([
            'user_id'            => $assignee->id,
            'assigned_by'        => null,
            'contact_message_id' => $contactMessageId,
            'title'              => $title,
            'description'        => $description,
            'due_at'             => $due_at,
        ]);

        $task->solicitor_id = $solicitorId;
        $task->owner()->associate($owner);
        $task->save();

        $this->record($history, $task, 'Task added — ' . $this->taskDetails($task), 'added');

        return $task;
    }

    public function completeTask(HistoryService $history, Task $task) {
        $task->completed_at = now();
        $task->save();

        $actor = $this->userName(auth()->user()) ?? 'Unknown';

        $this->record($history, $task, 'Task completed — ' . $this->taskDetails($task) . ' • Completed by: ' . $actor, 'completed');
    }

    public function deleteTask(HistoryService $history, Task $task) {
        $actor = $this->userName(auth()->user()) ?? 'Unknown';

        // Record before deleting so the task details are still available.
        $this->record($history, $task, 'Task deleted — ' . $this->taskDetails($task) . ' • Deleted by: ' . $actor, 'deleted');

        $task->delete();
    }

    /**
     * Write a task history entry against the file it belongs to. Falls back to
     * the assignee's own history when the task is not attached to a file.
     */
    private function record(HistoryService $history, Task $task, string $event, string $action): void {
        $target = $this->historyTarget($task);

        if ($target) {
            $history->add($target, $event, $this->taskContext($task, $action));
        }
    }

    private function historyTarget(Task $task): ?HistoryOwner {
        $owner = $task->owner;

        if ($owner instanceof HistoryOwner) {
            return $owner;
        }

        return $task->user instanceof HistoryOwner ? $task->user : null;
    }

    /**
     * Human-readable summary stating the task title, description, due date,
     * assignor and assignee.
     */
    private function taskDetails(Task $task): string {
        $parts = [
            'Title: ' . ($task->title ?: '—'),
            'Description: ' . ($task->description ?: '—'),
            'Due: ' . ($task->due_at ? $task->due_at->format('d/m/Y H:i') : 'No due date'),
            'Assignor: ' . ($this->userName($task->assignor) ?? ($task->assigned_by ? 'Unknown' : 'System')),
            'Assignee: ' . ($this->userName($task->user) ?? 'Unassigned'),
        ];

        return implode(' • ', $parts);
    }

    /**
     * Structured task data stored alongside the history entry.
     */
    private function taskContext(Task $task, string $action): array {
        return [
            'action'      => $action,
            'task_id'     => $task->id,
            'title'       => $task->title,
            'description' => $task->description,
            'due_at'      => $task->due_at?->toIso8601String(),
            'assignor_id' => $task->assigned_by,
            'assignor'    => $this->userName($task->assignor),
            'assignee_id' => $task->user_id,
            'assignee'    => $this->userName($task->user),
        ];
    }

    private function userName(?User $user): ?string {
        if (! $user) {
            return null;
        }

        return trim("{$user->name_first} {$user->name_last}") ?: null;
    }
}
