<?php

namespace App\Domain\Scheduling\Services;

use App\Domain\Scheduling\Enums\AssignmentStatus;
use App\Domain\Scheduling\Events\AssignmentCreated;
use App\Domain\Scheduling\Events\AssignmentEnded;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class AssignmentService
{
    private const VALID_TRANSITIONS = [
        'active' => ['suspended', 'completed', 'cancelled'],
        'suspended' => ['active', 'cancelled'],
        'completed' => [],
        'cancelled' => [],
    ];

    public function assign(User $user, Model $assignable, array $data, User $creator): Assignment
    {
        return DB::transaction(function () use ($user, $assignable, $data, $creator) {
            // Check for duplicate active assignment.
            //
            // Keyed by user + assignable, which is a natural key that does not
            // depend on who is reading: scoped, an assignment already held in
            // the assignable's own branch would be invisible from another and
            // this would happily write a second active one.
            $exists = Assignment::withoutBranchScope()
                ->where('user_id', $user->id)
                ->where('assignable_type', $assignable->getMorphClass())
                ->where('assignable_id', $assignable->getKey())
                ->where('status', 'active')
                ->exists();

            if ($exists) {
                throw new DomainException(__('يوجد تكليف نشط بالفعل لهذا المستخدم في نفس السياق'));
            }

            $assignment = Assignment::create([
                'academy_id' => $creator->academy_id,
                // The assignment belongs to the branch of the thing it assigns
                // someone to — a group or a session — not to whichever branch
                // the person filing it was looking at. Null falls through to the
                // creating hook.
                'branch_id' => $assignable->getAttribute('branch_id'),
                'user_id' => $user->id,
                'assignable_type' => $assignable->getMorphClass(),
                'assignable_id' => $assignable->getKey(),
                'scope' => $data['scope'] ?? 'full',
                'status' => 'active',
                'role_label' => $data['role_label'] ?? null,
                'start_date' => $data['start_date'],
                'end_date' => $data['end_date'] ?? null,
                'start_time' => $data['start_time'] ?? null,
                'end_time' => $data['end_time'] ?? null,
                'is_primary' => $data['is_primary'] ?? false,
                'notes' => $data['notes'] ?? null,
                'schedule_days' => $data['schedule_days'] ?? [],
                'metadata' => $data['metadata'] ?? [],
                'created_by' => $creator->id,
            ]);

            AssignmentCreated::dispatch($assignment, $creator);

            return $assignment;
        });
    }

    public function changeStatus(Assignment $assignment, string $newStatus, ?string $reason = null, ?User $actor = null): Assignment
    {
        return DB::transaction(function () use ($assignment, $newStatus, $reason, $actor) {
            $currentStatus = $assignment->status->value;
            $allowed = self::VALID_TRANSITIONS[$currentStatus] ?? [];

            if (!in_array($newStatus, $allowed)) {
                throw new DomainException(
                    __('لا يمكن تغيير حالة التكليف من') . " '{$assignment->status->label()}' " . __('إلى') . " '" . AssignmentStatus::from($newStatus)->label() . "'"
                );
            }

            $assignment->update([
                'status' => $newStatus,
                'notes' => $reason
                    ? ($assignment->notes ? $assignment->notes . "\n" . $reason : $reason)
                    : $assignment->notes,
            ]);

            $fresh = $assignment->fresh();

            if (in_array($newStatus, ['completed', 'cancelled']) && $actor) {
                AssignmentEnded::dispatch($fresh, $actor);
            }

            return $fresh;
        });
    }

    public function resolveForSession(int $sessionId, int $groupId): Collection
    {
        // Session-level assignments override group-level
        $sessionAssignments = Assignment::where('assignable_type', \App\Domain\Training\Models\TrainingSession::class)
            ->where('assignable_id', $sessionId)
            ->where('status', 'active')
            ->get();

        if ($sessionAssignments->isNotEmpty()) {
            return $sessionAssignments;
        }

        // Fallback to group-level
        return Assignment::where('assignable_type', \App\Domain\Training\Models\TrainingGroup::class)
            ->where('assignable_id', $groupId)
            ->where('status', 'active')
            ->get();
    }

    public function getActiveForUser(int $userId): Collection
    {
        return Assignment::where('user_id', $userId)
            ->where('status', 'active')
            ->with('assignable')
            ->get();
    }
}
