<?php
namespace EasyChat\Library\Support;

use EasyChat\Library\Logger;
use EasyChat\Library\Queue\QueueManager;
use EasyChat\Model\ChatAssignment;
use EasyChat\Model\Manager;
use EasyChat\Model\Session;




class ChatAssignmentService {
    private $db;
    private $assignments;
    private $managers;
    private $policy;
    private $sessions;
    private $registry;

    public function __construct($registry) {
        $this->registry = $registry;
        $this->db = $registry->get('db');
        $this->assignments = new ChatAssignment($registry);
        $this->managers = new Manager($registry);
        $this->policy = new ChatAssignmentPolicy();
        $this->sessions = new Session($registry);
    }

    public function capabilities($actor_manager_id): array {
        $actor = $this->activeActor((int)$actor_manager_id);
        return $this->capabilitiesForActor($actor);
    }

    private function capabilitiesForActor(array $actor): array {
        return $this->policy->capabilities($actor);
    }

    public function change($chat_id, $target_manager_id, $assign, $actor_manager_id): array {
        $chat_id = (int)$chat_id;
        $target_manager_id = (int)$target_manager_id;
        $actor_manager_id = (int)$actor_manager_id;
        $assign = (bool)$assign;

        if ($chat_id <= 0 || !$this->sessions->getSessionById($chat_id)) {
            return ['success' => false, 'error' => 'chat_not_found'];
        }
        if ($target_manager_id <= 0) {
            return ['success' => false, 'error' => 'manager_required'];
        }

        $actor = $this->activeActor($actor_manager_id);
        if (!$actor) {
            return ['success' => false, 'error' => 'actor_disabled'];
        }
        $capabilities = $this->capabilitiesForActor($actor);
        if (!$this->policy->canChangeTarget($actor, $actor_manager_id, $target_manager_id)) {
            return ['success' => false, 'error' => 'assignment_forbidden'];
        }

        $target = $this->managers->getManagerById($target_manager_id);
        if (!$target) {
            return ['success' => false, 'error' => 'manager_not_found'];
        }
         
         
        if ($assign && (int)($target['status'] ?? 0) !== 1) {
            return ['success' => false, 'error' => 'manager_disabled'];
        }

        $transaction_open = false;
        try {
             
             
             
             
            $queue = $target_manager_id !== $actor_manager_id
                ? new QueueManager($this->registry)
                : null;
            $this->db->query('START TRANSACTION');
            $transaction_open = true;

            $changed = $assign
                ? $this->assignments->assignIfMissing($chat_id, $target_manager_id, $actor_manager_id, 'manual')
                : $this->assignments->unassign($chat_id, $target_manager_id);

            $target_active = (int)($target['status'] ?? 0) === 1;
            if ($assign && !$changed && !$this->assignments->isAssigned($chat_id, $target_manager_id)) {
                 
                 
                throw new \RuntimeException('chat_assignment_target_race');
            }

            if ($changed && $target_active && $target_manager_id !== $actor_manager_id) {
                $mode = $assign ? 'assigned' : 'unassigned';
                $channels = $assign ? ['fcm', 'webpush'] : ['fcm'];
                foreach ($channels as $channel) {
                     
                     
                    $job_id = $queue->pushUnique(
                        'chat_assignment_push',
                        [
                            'chat_id' => $chat_id,
                            'target_manager_id' => $target_manager_id,
                            'mode' => $mode,
                            'channel' => $channel,
                        ],
                        'chat_assignment_push:' . $mode . ':' . $channel . ':' . $chat_id . ':' . $target_manager_id,
                        0,
                        90
                    );
                    if ($job_id <= 0) {
                        throw new \RuntimeException('chat_assignment_push_enqueue_failed');
                    }
                }
            }

            $this->db->query('COMMIT');
            $transaction_open = false;
        } catch (\Throwable $e) {
            if ($transaction_open) {
                try {
                    $this->db->query('ROLLBACK');
                } catch (\Throwable $ignored) {
                }
            }
            Logger::error(
                'Chat assignment transaction failed for chat #' . $chat_id
                . ', target #' . $target_manager_id,
                'chat-assignment.log'
            );
            return ['success' => false, 'error' => 'assignment_storage_failed'];
        }

        return [
            'success' => true,
            'changed' => (bool)$changed,
            'assignment' => $this->assignments->getSnapshot($chat_id, $actor_manager_id),
            'assignment_capabilities' => $capabilities,
        ];
    }

    private function activeActor(int $manager_id): array {
        if ($manager_id <= 0) {
            return [];
        }
        $manager = $this->managers->getManagerById($manager_id);
        return $manager && (int)($manager['status'] ?? 0) === 1 ? $manager : [];
    }
}
