<?php
namespace EasyChat\Library\Operator;

use EasyChat\Library\Security\AuthRateLimiter;
use EasyChat\Library\Security\PasswordCredentialService;
use EasyChat\Model\AppSession;
use EasyChat\Model\Manager;

final class OperatorSelfService {
    private $registry;
    private $credentials;

    public function __construct($registry) {
        $this->registry = $registry;
        $this->credentials = new PasswordCredentialService();
    }

     
    public function profile(int $manager_id): array {
        $manager = (new Manager($this->registry))->getManagerById($manager_id);
        if (!$manager || (int)($manager['status'] ?? 0) !== 1) {
            return [];
        }

        $image = basename((string)($manager['image'] ?? ''));
        $version = '';
        if ($image !== '' && is_file(DIR_AVATARS . $image)) {
            $version = substr(hash_file('sha256', DIR_AVATARS . $image), 0, 16);
        }

        return [
            'manager_id' => (int)$manager['id'],
            'username' => (string)$manager['username'],
            'name' => (string)$manager['name'],
            'role' => (string)$manager['role'],
            'role_label' => (string)$manager['role'] === 'admin' ? 'Администратор' : 'Оператор',
            'avatar_url' => $image !== '' ? HTTP_AVATARS . rawurlencode($image) . ($version !== '' ? '?v=' . $version : '') : '',
            'avatar_version' => $version,
            'password_policy' => $this->credentials->publicPolicy(),
        ];
    }

    


    public function changePassword(
        int $manager_id,
        string $current_password,
        string $new_password,
        string $confirmation,
        string $ip,
        int $except_app_session_id = 0
    ): array {
        $manager = (new Manager($this->registry))->getManagerById($manager_id);
        if (!$manager || (int)($manager['status'] ?? 0) !== 1) {
            return $this->error('operator_not_found', 'Оператор не найден.');
        }

        $limiter = new AuthRateLimiter($this->registry);
        $limit = $limiter->status('password_change', $manager_id, $ip);
        if (empty($limit['allowed'])) {
            return $this->error(
                'password_rate_limited',
                'Слишком много неудачных попыток. Повторите через несколько минут.'
            );
        }
        if (!$this->credentials->verify($current_password, (string)$manager['password'])) {
            $limiter->recordFailure('password_change', $manager_id, $ip);
            return $this->error('current_password_invalid', 'Текущий пароль указан неверно.');
        }
        if (!hash_equals($new_password, $confirmation)) {
            return $this->error('password_confirmation_mismatch', 'Новые пароли не совпадают.');
        }
        if ($this->credentials->verify($new_password, (string)$manager['password'])) {
            return $this->error('password_not_changed', 'Новый пароль должен отличаться от текущего.');
        }

        $validation = $this->credentials->validate(
            $new_password,
            (string)$manager['username'],
            (string)$manager['name']
        );
        if (empty($validation['valid'])) {
            return $this->error((string)$validation['code'], (string)$validation['message']);
        }

        $hash = $this->credentials->hash($new_password);
        $db = $this->registry->get('db');
        $transaction_open = false;
        try {
            $db->query('START TRANSACTION');
            $transaction_open = true;

            $manager_model = new Manager($this->registry);
            $manager_model->updatePasswordHash($manager_id, $hash);
            (new AppSession($this->registry))->revokeManagerSessions(
                $manager_id,
                'password_changed',
                $except_app_session_id
            );
            if ($manager_id === 1) {
                $manager_model->markAdminPasswordCustomized();
            }

            $db->query('COMMIT');
            $transaction_open = false;
        } catch (\Throwable $e) {
            if ($transaction_open) {
                try {
                    $db->query('ROLLBACK');
                } catch (\Throwable $ignored) {
                }
            }
            throw $e;
        }
        $limiter->clear('password_change', $manager_id, $ip);

        return ['success' => true, 'password_hash' => $hash];
    }

     
    private function error(string $code, string $message): array {
        return ['success' => false, 'code' => $code, 'error' => $message];
    }
}
