<?php

declare(strict_types=1);

require_once __DIR__ . '/PasarguardApiClient.php';
require_once __DIR__ . '/PasarguardConfig.php';

/**
 * High-level Pasarguard user operations for the bot.
 */
class PasarguardService
{
    /** @var PasarguardApiClient */
    private $client;

    /** @var array<string, mixed> */
    private $serverConfig;

    /** @param array<string, mixed> $serverConfig */
    public function __construct(array $serverConfig)
    {
        $this->serverConfig = $serverConfig;
        $this->client = new PasarguardApiClient($serverConfig);
    }

    /** @return object{success:bool,msg?:string,sub_link?:string,vray_links?:string[]} */
    public function createUser(string $remark, int $volumeGb, int $days, array $planSettings)
    {
        $templateId = (int) ($planSettings['template_id'] ?? 0);
        if ($templateId <= 0) {
            return (object) ['success' => false, 'msg' => 'User Template برای این پلن انتخاب نشده'];
        }

        $response = $this->client->request('POST', '/api/user/from_template', [
            'user_template_id' => $templateId,
            'username' => $remark,
        ]);

        if (!$response->success) {
            $msg = strtolower((string) ($response->msg ?? ''));
            if (strpos($msg, 'exist') !== false || strpos($msg, 'already') !== false || strpos($msg, 'duplicate') !== false) {
                $existing = $this->getUser($remark);
                if ($existing) {
                    return $this->buildCreateResponse($existing);
                }
            }
            return (object) ['success' => false, 'msg' => $response->msg ?? 'create failed'];
        }

        $result = $this->buildCreateResponse($response->data, (string) $remark);
        if (!$result->success) {
            return $result;
        }

        // Apply plan volume/days on top of the template defaults when specified
        if ($volumeGb > 0 || $days > 0) {
            $updatePayload = [];
            if ($volumeGb > 0) {
                $updatePayload['data_limit'] = $volumeGb * 1073741824;
            }
            if ($days > 0) {
                $updatePayload['expire'] = time() + 86400 * $days;
                $updatePayload['status'] = 'active';
            }
            if (!empty($updatePayload)) {
                $this->client->request('PUT', '/api/user/' . rawurlencode($remark), $updatePayload);
            }
        }

        return $result;
    }

    /** @return object|null */
    public function getUser(string $remark)
    {
        $path = '/api/user/' . rawurlencode($remark);
        $response = $this->client->request('GET', $path);
        if (!$response->success) {
            return null;
        }

        return is_object($response->data) ? $response->data : null;
    }

    /** @param array<string, mixed> $info @return object{success:bool,msg?:string} */
    public function editUser(string $remark, array $info)
    {
        $configInfo = $this->getUser($remark);
        if (!$configInfo) {
            return (object) ['success' => false, 'msg' => 'User not found'];
        }

        $expireTime = (int) ($configInfo->expire ?? 0);
        $volume = (int) ($configInfo->data_limit ?? 0);

        if (isset($info['plus_day'])) {
            if ($expireTime < time()) {
                $expireTime = time() + (86400 * (int) $info['plus_day']);
            } else {
                $expireTime += (86400 * (int) $info['plus_day']);
            }
        } elseif (isset($info['days'])) {
            $expireTime = time() + (86400 * (int) $info['days']);
        }

        if (isset($info['plus_volume'])) {
            $volume += (int) $info['plus_volume'] * 1073741824;
        } elseif (isset($info['volume'])) {
            $volume = (int) $info['volume'] * 1073741824;
            $reset = $this->resetTraffic($remark);
            if (!$reset->success) {
                return $reset;
            }
        }

        $payload = [
            'expire' => $expireTime > 0 ? $expireTime : null,
            'data_limit' => $volume > 0 ? $volume : null,
            'status' => 'active',
        ];

        $response = $this->client->request('PUT', '/api/user/' . rawurlencode($remark), $payload);
        if (!$response->success) {
            return (object) ['success' => false, 'msg' => $response->msg ?? 'update failed'];
        }

        return (object) ['success' => true];
    }

    /** @return object{success:bool,msg?:string} */
    public function deleteUser(string $remark)
    {
        $response = $this->client->request('DELETE', '/api/user/' . rawurlencode($remark));
        if (!$response->success) {
            return (object) ['success' => false, 'msg' => $response->msg ?? 'delete failed'];
        }

        return (object) ['success' => true];
    }

    /** @return object{success:bool,msg?:string} */
    public function resetTraffic(string $remark)
    {
        $response = $this->client->request('POST', '/api/user/' . rawurlencode($remark) . '/reset');
        if (!$response->success) {
            return (object) ['success' => false, 'msg' => $response->msg ?? 'reset failed'];
        }

        return (object) ['success' => true];
    }

    /** @return object{success:bool,msg?:string,subscription_url?:string,links?:string[]} */
    public function revokeSubscription(string $remark)
    {
        $response = $this->client->request('POST', '/api/user/' . rawurlencode($remark) . '/revoke_sub');
        if (!$response->success) {
            return (object) ['success' => false, 'msg' => $response->msg ?? 'revoke failed'];
        }
        $user = $this->getUser($remark);
        if (!$user) {
            return (object) ['success' => false, 'msg' => 'User not found after revoke'];
        }

        $panelUrl = (string) ($this->serverConfig['panel_url'] ?? '');
        $subUrl = PasarguardConfig::buildSubscriptionUrl($panelUrl, (string) ($user->subscription_url ?? ''));

        return (object) [
            'success' => true,
            'subscription_url' => (string) ($user->subscription_url ?? ''),
            'links' => $subUrl !== '' ? [$subUrl] : [],
        ];
    }

    /** @return object{success:bool,msg?:string} */
    public function toggleState(string $remark)
    {
        $configInfo = $this->getUser($remark);
        if (!$configInfo) {
            return (object) ['success' => false, 'msg' => 'User not found'];
        }
        $newStatus = ($configInfo->status ?? '') === 'active' ? 'disabled' : 'active';
        $response = $this->client->request('PUT', '/api/user/' . rawurlencode($remark), [
            'status' => $newStatus,
        ]);
        if (!$response->success) {
            return (object) ['success' => false, 'msg' => $response->msg ?? 'toggle failed'];
        }

        return (object) ['success' => true];
    }

    /** @return array<int, object{id:int,name:?string}> */
    public function listUserTemplates(): array
    {
        $response = $this->client->request('GET', '/api/user_templates/simple');
        if (!$response->success || !is_object($response->data)) {
            return [];
        }
        $templates = $response->data->templates ?? [];

        return is_array($templates) ? $templates : [];
    }

    /**
     * List available groups from the Pasarguard panel.
     * Returns array of objects with at least {id, title} or {id, name}.
     * @return array<int, object>
     */
    public function listGroups(): array
    {
        // Try common endpoints used by Pasarguard panels
        foreach (['/api/inbound/group', '/api/group', '/api/groups'] as $endpoint) {
            $response = $this->client->request('GET', $endpoint);
            if (!$response->success) continue;

            $data = $response->data;
            // Might be an array directly or wrapped in {groups: [...]}
            if (is_array($data) && !empty($data)) {
                return $data;
            }
            if (is_object($data)) {
                foreach (['groups', 'items', 'data'] as $key) {
                    if (isset($data->$key) && is_array($data->$key) && !empty($data->$key)) {
                        return $data->$key;
                    }
                }
            }
        }
        return [];
    }

    /**
     * Create a Pasarguard user assigned to a specific group (no template).
     * All plan specs (volume, days) come from the WizWiz plan definition.
     * @return object{success:bool,msg?:string,sub_link?:string,vray_links?:string[]}
     */
    public function createUserWithGroup(string $remark, int $volumeGb, int $days, int $groupId, string $subId = ''): object
    {
        $payload = [
            'username' => $remark,
            'status'   => 'active',
        ];
        if ($volumeGb > 0) {
            $payload['data_limit'] = $volumeGb * 1073741824;
        }
        if ($days > 0) {
            $payload['expire'] = time() + 86400 * $days;
        }
        if ($groupId > 0) {
            // Try common field names for group assignment
            $payload['group_ids']         = [$groupId];
            $payload['inbound_group_ids'] = [$groupId];
        }
        // Preserve subscription ID so the sub link path stays identical to the source panel
        if ($subId !== '') {
            $payload['sub_id'] = $subId;
        }

        $response = $this->client->request('POST', '/api/user', $payload);

        if (!$response->success) {
            $msg = strtolower((string) ($response->msg ?? ''));
            if (strpos($msg, 'exist') !== false || strpos($msg, 'already') !== false || strpos($msg, 'duplicate') !== false) {
                $existing = $this->getUser($remark);
                if ($existing) {
                    return $this->buildCreateResponse($existing);
                }
            }
            return (object) ['success' => false, 'msg' => $response->msg ?? 'create failed'];
        }

        return $this->buildCreateResponse($response->data, $remark);
    }

    /** @return object{success:bool,msg?:string,sub_link?:string,vray_links?:string[],remark?:string} */
    private function buildCreateResponse($data, string $remarkHint = '')
    {
        if (!is_object($data)) {
            return (object) ['success' => false, 'msg' => 'Invalid panel response'];
        }
        $username = (string) ($data->username ?? $remarkHint);
        if ($username === '') {
            return (object) ['success' => false, 'msg' => 'Invalid panel response'];
        }

        $subUrl = (string) ($data->subscription_url ?? '');
        if ($subUrl === '') {
            $fresh = $this->getUser($username);
            if (is_object($fresh)) {
                $data = $fresh;
                $subUrl = (string) ($fresh->subscription_url ?? '');
            }
        }

        $panelUrl = (string) ($this->serverConfig['panel_url'] ?? '');
        $subPath = PasarguardConfig::extractSubPath($subUrl);
        $subLink = PasarguardConfig::buildSubscriptionUrl($panelUrl, $subUrl);

        return (object) [
            'success' => true,
            'remark' => $username,
            'sub_link' => $subPath,
            'vray_links' => $subLink !== '' ? [$subLink] : [],
        ];
    }
}
