<?php
namespace App\Controllers;

use App\Core\Controller;
use App\Models\KeyModel;
use App\Models\SystemModel;

class ApiController extends Controller
{
    private function requireSiteAuthorization(): bool
    {
        return true;
    }

    public function ping(): void
    {
        $this->apiOk([
            'server_time' => time(),
            'request_id' => (string)($_SERVER['APP_REQUEST_ID'] ?? ''),
        ], 'ok');
    }

    public function login(): void
    {
        $this->handleLogin(null);
    }

    public function keyQuery(): void
    {
        if (!$this->requireSiteAuthorization() || !$this->requireApiTokenAuth()) {
            return;
        }

        $rate = app_rate_limit_consume('api_v1_key_query', 60, 60, app_client_ip());
        if (!empty($rate['limited'])) {
            $this->apiFail('RATE_LIMIT', app_zh('请求过于频繁，请稍后再试'), 429);
            return;
        }

        $in = $this->input();
        $userKey = app_clean_text((string)($in['user_key'] ?? ($in['key'] ?? '')), 128);
        if ($userKey === '') {
            $this->apiFail('PARAMS_REQUIRED', app_zh('参数不足：请传 user_key'));
            return;
        }
        if ($this->rejectReplayedOptionalNonce($in, 'api_key_query|' . $userKey . '|' . app_client_ip())) {
            return;
        }

        $model = new KeyModel();
        $key = $model->findByUserKey($userKey);
        if (!$key) {
            $this->apiFail('KEY_NOT_FOUND', app_zh('卡密不存在'));
            return;
        }

        $system = (new SystemModel())->get();
        $devices = app_normalize_device_list((string)($key['devices'] ?? ''));

        app_log_api_event('api_key_query', [
            'user_key' => '[redacted]',
        ]);

        $this->apiOk([
            'game' => (string)($key['game'] ?? ''),
            'user_key' => (string)($key['user_key'] ?? ''),
            'status' => (string)($key['status'] ?? ''),
            'activated' => !empty($key['expired_at']),
            'expired_at' => (string)($key['expired_at'] ?? ''),
            'devices_count' => count($devices),
            'devices' => $devices,
            'device_list' => implode(',', $devices),
            'max_devices' => (int)($key['max_devices'] ?? 0),
            'allow_self_unbind' => !empty($system['allow_self_unbind']),
        ], 'ok');
    }

    public function keyUnbind(): void
    {
        if (!$this->requireSiteAuthorization() || !$this->requireApiTokenAuth()) {
            return;
        }

        $rate = app_rate_limit_consume('api_v1_key_unbind', 20, 60, app_client_ip());
        if (!empty($rate['limited'])) {
            $this->apiFail('RATE_LIMIT', app_zh('请求过于频繁，请稍后再试'), 429);
            return;
        }

        $in = $this->input();
        $userKey = app_clean_text((string)($in['user_key'] ?? ($in['key'] ?? '')), 128);
        if ($userKey === '') {
            $this->apiFail('PARAMS_REQUIRED', app_zh('参数不足：请传 user_key'));
            return;
        }
        if ($this->rejectReplayedOptionalNonce($in, 'api_key_unbind|' . $userKey . '|' . app_client_ip())) {
            return;
        }

        $system = (new SystemModel())->get();
        if (empty($system['allow_self_unbind'])) {
            $this->apiFail('UNBIND_DISABLED', app_zh('站点未开启自助解绑'));
            return;
        }

        $model = new KeyModel();
        $key = $model->findByUserKey($userKey);
        if (!$key) {
            $this->apiFail('KEY_NOT_FOUND', app_zh('卡密不存在'));
            return;
        }

        $model->update((int)$key['id'], ['devices' => null]);
        app_log_audit_event('api_key_unbind', [
            'user_key' => '[redacted]',
            'key_id' => (int)$key['id'],
        ]);
        $this->apiOk(['user_key' => $userKey], app_zh('解绑成功'));
    }

    public function notices(): void
    {
        $this->apiOk(remote_notice_data(true), 'ok');
    }

    private function apiOk(array $data, string $message = 'ok'): void
    {
        $this->json([
            'status' => true,
            'code' => 'OK',
            'message' => $message,
            'reason' => $message,
            'data' => $data,
        ]);
    }

    private function apiFail(string $code, string $message, int $http = 200): void
    {
        http_response_code($http);
        $this->json([
            'status' => false,
            'code' => $code,
            'message' => $message,
            'reason' => $message,
        ]);
    }

    private function input(): array
    {
        return app_request_data();
    }

    private function handleLogin(?string $forcedGame = null): void
    {
        $rate = app_rate_limit_consume('api_v1_login', 240, 60, app_client_ip());
        if (!empty($rate['limited'])) {
            $this->apiFail('RATE_LIMIT', app_zh('请求过于频繁，请稍后再试'));
            return;
        }

        $in = $this->input();
        $game = app_clean_text((string)($forcedGame ?? ($in['game'] ?? '')), 64);
        $userKey = app_clean_text((string)($in['user_key'] ?? ($in['key'] ?? '')), 128);
        $serialInput = app_clean_text((string)($in['serial'] ?? ''), 128);
        $serialContext = app_resolve_login_device_serial_context($serialInput);
        $serial = (string)($serialContext['serial'] ?? '');

        if ($game === '' || $userKey === '') {
            $this->apiFail('PARAMS_REQUIRED', app_zh('参数不足：请传 game 和 user_key'));
            return;
        }
        if ($serial === '') {
            app_log_api_event('api_login_missing_serial', [
                'game' => $game,
                'user_key' => $userKey !== '' ? '[provided]' : '',
            ]);
            $this->apiFail('SERIAL_REQUIRED', app_zh('设备码（serial）不能为空，浏览器访问会自动绑定当前浏览器'));
            return;
        }
        $nonce = app_clean_text((string)($in['nonce'] ?? ''), 96);
        if (!app_consume_optional_client_nonce('api_login|' . $game . '|' . $userKey . '|' . app_client_ip(), $nonce, 60)) {
            $this->apiFail('NONCE_REPLAY', app_zh('请求已重复，请稍后重试'));
            return;
        }

        $system = (new SystemModel())->get();
        if (($system['mod_status'] ?? 'on') === 'off') {
            $reason = trim((string)($system['maintenance_reason'] ?? app_zh('维护中')));
            $this->apiFail('MAINTENANCE', $reason === '' ? app_zh('维护中') : $reason);
            return;
        }

        $keyModel = new KeyModel();
        try {
            $consume = $keyModel->consumeLogin($userKey, $game, $serial, (array)($serialContext['aliases'] ?? []));
        } catch (\Throwable $e) {
            app_log_security_event('api_login_internal_error', [
                'reason' => $e->getMessage(),
                'game' => $game,
            ]);
            $this->apiFail('INTERNAL_ERROR', app_zh('服务器内部错误'), 500);
            return;
        }

        if (empty($consume['ok'])) {
            $code = (string)($consume['code'] ?? 'KEY_NOT_FOUND');
            app_log_security_event('api_login_failed', [
                'game' => $game,
                'reason' => $code,
                'ip' => app_client_ip(),
            ]);
            $this->apiFail($code, $this->loginErrorMessage($code));
            return;
        }

        $key = (array)($consume['key'] ?? []);
        $devices = (array)($consume['devices'] ?? []);
        $serverTime = (int)($consume['server_time'] ?? time());
        $tokenV1 = token_for($game, $userKey, $serial);
        $tokenV2 = token_v2_for($game, $userKey, $serial);
        app_log_api_event('api_login_success', [
            'game' => $game,
            'serial' => $serial,
            'user_key' => '[redacted]',
        ]);

        $payload = [
            'game' => $game,
            'user_key' => $userKey,
            'serial_used' => $serial,
            'token' => $tokenV1,
            'token_v1' => $tokenV1,
            'token_v2' => $tokenV2,
            'token_v2_expire' => time() + 900,
            'expired_at' => (string)($key['expired_at'] ?? ''),
            'max_devices' => (int)($key['max_devices'] ?? 1),
            'devices_count' => count($devices),
            'server_time' => $serverTime,
            'compat' => [
                'MOD_NAME' => (string)($system['mod_name'] ?? ''),
                'MOD_STATUS' => (string)($system['notice_status'] ?? ''),
                'FLOTING_TEST' => (string)($system['notice_text'] ?? ''),
                'BHATIA_EXP' => (string)($key['expired_at'] ?? ''),
                'BHATIA_SLOT' => (int)($key['max_devices'] ?? 1),
                'rng' => $serverTime,
            ],
        ];

        $this->apiOk($payload, app_zh('登录成功'));
    }

    private function loginErrorMessage(string $code): string
    {
        if ($code === 'SERIAL_REQUIRED') {
            return app_zh('设备码（serial）不能为空');
        }
        if ($code === 'KEY_DISABLED') {
            return app_zh('卡密已禁用');
        }
        if ($code === 'KEY_EXPIRED') {
            return app_zh('卡密已过期');
        }
        if ($code === 'DEVICE_LIMIT') {
            return app_zh('设备数量已达上限');
        }
        return app_zh('卡密不存在');
    }

    private function requireApiTokenAuth(): bool
    {
        $registry = app_api_key_registry();
        if ($registry === []) {
            $this->apiFail('API_AUTH_NOT_CONFIGURED', app_zh('接口鉴权未配置'), 503);
            return false;
        }

        $signed = app_verify_signed_api_request();
        if (!empty($signed['ok'])) {
            app_log_api_event('api_auth_signed_ok', [
                'key_id' => (string)($signed['key_id'] ?? ''),
            ]);
            return true;
        }

        $token = trim((string)($_SERVER['HTTP_X_API_TOKEN'] ?? ''));
        if ($token === '') {
            $token = trim((string)($_SERVER['HTTP_X_API_KEY'] ?? ''));
        }
        if ($token === '') {
            $token = trim((string)($_SERVER['HTTP_API_KEY'] ?? ''));
        }
        if ($token === '') {
            $auth = trim((string)($_SERVER['HTTP_AUTHORIZATION'] ?? ''));
            if (stripos($auth, 'Bearer ') === 0) {
                $token = trim(substr($auth, 7));
            }
        }

        if ($token === '') {
            app_log_security_event('api_auth_failed', [
                'reason' => (string)($signed['reason'] ?? 'missing_token'),
            ]);
            $this->apiFail('UNAUTHORIZED', app_zh('未授权访问'), 401);
            return false;
        }

        foreach ($registry as $keyId => $secret) {
            if ($secret !== '' && hash_equals($secret, $token)) {
                app_log_api_event('api_auth_legacy_ok', [
                    'key_id' => (string)$keyId,
                ]);
                return true;
            }
        }

        app_log_security_event('api_auth_failed', [
            'reason' => (string)($signed['reason'] ?? 'token_mismatch'),
        ]);
        $this->apiFail('UNAUTHORIZED', app_zh('未授权访问'), 401);
        return false;
    }

    private function rejectReplayedOptionalNonce(array $input, string $scope): bool
    {
        $nonce = app_clean_text((string)($input['nonce'] ?? ''), 96);
        if (app_consume_optional_client_nonce($scope, $nonce, 60)) {
            return false;
        }

        $this->apiFail('NONCE_REPLAY', app_zh('请求已重复，请稍后重试'));
        return true;
    }
}
