<?php
declare(strict_types=1);

date_default_timezone_set('Asia/Dhaka');

const AVIATOR_ADMIN_PIN = '123456'; // Change this before uploading.
const AVIATOR_WAIT_SECONDS = 10;
const AVIATOR_AFTER_CRASH_SECONDS = 4;
const AVIATOR_CURRENCY = 'BDT';

function aviator_data_dir(): string {
    $dir = __DIR__ . '/data';
    if (!is_dir($dir)) {
        @mkdir($dir, 0777, true);
    }
    return $dir;
}

function aviator_path(string $name): string {
    return aviator_data_dir() . '/' . $name;
}

function aviator_now(): int {
    return time();
}

function aviator_h(string $value): string {
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}

function aviator_client_ip(): string {
    $keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
    foreach ($keys as $key) {
        if (!empty($_SERVER[$key])) {
            $value = trim((string) $_SERVER[$key]);
            if ($key === 'HTTP_X_FORWARDED_FOR') {
                $parts = array_map('trim', explode(',', $value));
                $value = $parts[0] ?? $value;
            }
            if (filter_var($value, FILTER_VALIDATE_IP)) {
                return $value;
            }
        }
    }
    return '0.0.0.0';
}

function aviator_json_default(string $name): array {
    if ($name === 'visitors.json') {
        return ['visitors' => []];
    }
    if ($name === 'round.json') {
        return ['current' => null];
    }
    if ($name === 'history.json') {
        return ['history' => []];
    }
    if ($name === 'bets.json') {
        return ['bets' => []];
    }
    return [];
}

function aviator_read_json_file(string $path, array $default): array {
    if (!file_exists($path)) {
        file_put_contents($path, json_encode($default, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    }
    $raw = file_get_contents($path);
    $data = json_decode((string) $raw, true);
    return is_array($data) ? $data : $default;
}

function aviator_write_json_file(string $path, array $data): void {
    $tmp = $path . '.tmp';
    file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
    rename($tmp, $path);
}

function aviator_with_json(string $name, callable $callback): array {
    $path = aviator_path($name);
    $default = aviator_json_default($name);
    $fp = fopen($path, 'c+');
    if (!$fp) {
        return ['ok' => false, 'message' => 'Cannot open data file.'];
    }
    flock($fp, LOCK_EX);
    rewind($fp);
    $raw = stream_get_contents($fp);
    $data = json_decode($raw ?: json_encode($default), true);
    if (!is_array($data)) {
        $data = $default;
    }
    $result = $callback($data);
    ftruncate($fp, 0);
    rewind($fp);
    fwrite($fp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);
    return is_array($result) ? $result : ['ok' => true];
}

function aviator_generate_visitor_id(array $visitors): string {
    do {
        $id = (string) random_int(100000, 999999);
        $exists = false;
        foreach ($visitors as $visitor) {
            if (is_array($visitor) && (string) ($visitor['id'] ?? '') === $id) {
                $exists = true;
                break;
            }
        }
    } while ($exists);
    return $id;
}

function aviator_default_visitor(string $ip, string $id): array {
    $now = date('c');
    return [
        'id' => $id,
        'ip' => $ip,
        'balance' => 0.00,
        'status' => 'ACTIVE',
        'restrict_limit' => 0.00,
        'total_added' => 0.00,
        'total_bet' => 0.00,
        'total_win' => 0.00,
        'created_at' => $now,
        'updated_at' => $now,
    ];
}

function aviator_normalize_visitor(array $visitor, string $ip, string $id): array {
    $visitor['id'] = preg_match('/^\d{6}$/', (string) ($visitor['id'] ?? '')) ? (string) $visitor['id'] : $id;
    $visitor['ip'] = (string) ($visitor['ip'] ?? $ip);
    $visitor['balance'] = round(max(0, (float) ($visitor['balance'] ?? 0)), 2);
    $visitor['status'] = strtoupper((string) ($visitor['status'] ?? 'ACTIVE')) === 'RESTRICTED' ? 'RESTRICTED' : 'ACTIVE';
    $visitor['restrict_limit'] = round(max(0, (float) ($visitor['restrict_limit'] ?? 0)), 2);
    $visitor['total_added'] = round(max(0, (float) ($visitor['total_added'] ?? 0)), 2);
    $visitor['total_bet'] = round(max(0, (float) ($visitor['total_bet'] ?? 0)), 2);
    $visitor['total_win'] = round(max(0, (float) ($visitor['total_win'] ?? 0)), 2);
    $visitor['created_at'] = (string) ($visitor['created_at'] ?? date('c'));
    $visitor['updated_at'] = date('c');
    return $visitor;
}

function aviator_apply_restriction(array &$visitor): void {
    $limit = (float) ($visitor['restrict_limit'] ?? 0);
    $balance = (float) ($visitor['balance'] ?? 0);
    if ($limit > 0 && $balance >= $limit) {
        $visitor['status'] = 'RESTRICTED';
    }
}

function aviator_ensure_visitor_by_ip(string $ip): array {
    $result = aviator_with_json('visitors.json', function (&$data) use ($ip): array {
        if (!isset($data['visitors']) || !is_array($data['visitors'])) {
            $data['visitors'] = [];
        }
        if (!isset($data['visitors'][$ip]) || !is_array($data['visitors'][$ip])) {
            $id = aviator_generate_visitor_id($data['visitors']);
            $data['visitors'][$ip] = aviator_default_visitor($ip, $id);
        } else {
            $currentId = (string) ($data['visitors'][$ip]['id'] ?? '');
            if (!preg_match('/^\d{6}$/', $currentId)) {
                $currentId = aviator_generate_visitor_id($data['visitors']);
            }
            $data['visitors'][$ip] = aviator_normalize_visitor($data['visitors'][$ip], $ip, $currentId);
        }
        aviator_apply_restriction($data['visitors'][$ip]);
        return ['ok' => true, 'visitor' => $data['visitors'][$ip]];
    });
    return $result['visitor'] ?? aviator_default_visitor($ip, '000000');
}

function aviator_find_visitor_by_id(string $id): ?array {
    $id = preg_replace('/\D/', '', $id);
    if (strlen($id) !== 6) {
        return null;
    }
    $data = aviator_read_json_file(aviator_path('visitors.json'), aviator_json_default('visitors.json'));
    foreach (($data['visitors'] ?? []) as $ip => $visitor) {
        if (is_array($visitor) && (string) ($visitor['id'] ?? '') === $id) {
            $visitor['ip_key'] = (string) $ip;
            return $visitor;
        }
    }
    return null;
}

function aviator_update_visitor_by_id(string $id, callable $callback): array {
    $id = preg_replace('/\D/', '', $id);
    if (strlen($id) !== 6) {
        return ['ok' => false, 'message' => 'Invalid 6 digit ID.'];
    }
    return aviator_with_json('visitors.json', function (&$data) use ($id, $callback): array {
        if (!isset($data['visitors']) || !is_array($data['visitors'])) {
            $data['visitors'] = [];
        }
        foreach ($data['visitors'] as $ip => &$visitor) {
            if (is_array($visitor) && (string) ($visitor['id'] ?? '') === $id) {
                $visitor = aviator_normalize_visitor($visitor, (string) $ip, $id);
                $callback($visitor);
                $visitor['balance'] = round(max(0, (float) ($visitor['balance'] ?? 0)), 2);
                $visitor['restrict_limit'] = round(max(0, (float) ($visitor['restrict_limit'] ?? 0)), 2);
                $visitor['total_added'] = round(max(0, (float) ($visitor['total_added'] ?? 0)), 2);
                $visitor['total_bet'] = round(max(0, (float) ($visitor['total_bet'] ?? 0)), 2);
                $visitor['total_win'] = round(max(0, (float) ($visitor['total_win'] ?? 0)), 2);
                $visitor['updated_at'] = date('c');
                aviator_apply_restriction($visitor);
                return ['ok' => true, 'visitor' => $visitor];
            }
        }
        return ['ok' => false, 'message' => 'User ID not found. Open the main page once to create the ID first.'];
    });
}

function aviator_amount($value): float {
    return round(max(0, (float) $value), 2);
}

function aviator_random_crash_value(): float {
    $roll = random_int(1, 100);
    if ($roll <= 45) {
        return round(random_int(101, 149) / 100, 2);
    }
    if ($roll <= 80) {
        return round(random_int(150, 299) / 100, 2);
    }
    if ($roll <= 95) {
        return round(random_int(300, 999) / 100, 2);
    }
    return round(random_int(1000, 5000) / 100, 2);
}

function aviator_round_duration(float $crashValue): int {
    $ms = (int) round(2500 + min(9000, ($crashValue - 1.0) * 1700));
    return max(2200, min(11500, $ms));
}

function aviator_create_round(bool $force = false): array {
    $now = aviator_now();
    $crash = aviator_random_crash_value();
    $durationMs = aviator_round_duration($crash);
    $round = [
        'round_id' => bin2hex(random_bytes(6)),
        'crash_value' => $crash,
        'generated_at' => $now,
        'starts_at' => $now + AVIATOR_WAIT_SECONDS,
        'duration_ms' => $durationMs,
        'ends_at' => $now + AVIATOR_WAIT_SECONDS + (int) ceil($durationMs / 1000),
    ];

    aviator_with_json('round.json', function (&$data) use ($round): array {
        $data['current'] = $round;
        return ['ok' => true, 'round' => $round];
    });

    return $round;
}

function aviator_push_history(float $value, string $roundId): void {
    aviator_with_json('history.json', function (&$data) use ($value, $roundId): array {
        if (!isset($data['history']) || !is_array($data['history'])) {
            $data['history'] = [];
        }
        foreach ($data['history'] as $item) {
            if (is_array($item) && (string) ($item['round_id'] ?? '') === $roundId) {
                return ['ok' => true];
            }
        }
        array_unshift($data['history'], [
            'round_id' => $roundId,
            'value' => round($value, 2),
            'created_at' => date('c'),
        ]);
        $data['history'] = array_slice($data['history'], 0, 30);
        return ['ok' => true];
    });
}

function aviator_history(): array {
    $data = aviator_read_json_file(aviator_path('history.json'), aviator_json_default('history.json'));
    return is_array($data['history'] ?? null) ? $data['history'] : [];
}

function aviator_current_round(bool $autoGenerate = true): array {
    $data = aviator_read_json_file(aviator_path('round.json'), aviator_json_default('round.json'));
    $round = $data['current'] ?? null;
    $now = aviator_now();
    if (!is_array($round) || empty($round['round_id'])) {
        return $autoGenerate ? aviator_create_round(true) : [];
    }
    $endsAt = (int) ($round['ends_at'] ?? 0);
    if ($endsAt > 0 && $now > ($endsAt + AVIATOR_AFTER_CRASH_SECONDS)) {
        aviator_push_history((float) ($round['crash_value'] ?? 1), (string) ($round['round_id'] ?? ''));
        return $autoGenerate ? aviator_create_round(true) : $round;
    }
    return $round;
}

function aviator_round_state(array $round, bool $adminReveal = false): array {
    $now = aviator_now();
    $startsAt = (int) ($round['starts_at'] ?? ($now + AVIATOR_WAIT_SECONDS));
    $endsAt = (int) ($round['ends_at'] ?? $now);
    $durationMs = (int) ($round['duration_ms'] ?? 3000);
    $crash = round((float) ($round['crash_value'] ?? 1.00), 2);
    $status = 'waiting';
    if ($now >= $startsAt && $now < $endsAt) {
        $status = 'running';
    } elseif ($now >= $endsAt) {
        $status = 'crashed';
    }
    $elapsedMs = max(0, (int) round((microtime(true) - $startsAt) * 1000));
    $progress = $durationMs > 0 ? min(1, $elapsedMs / $durationMs) : 1;
    $currentMultiplier = 1.00;
    if ($status === 'running') {
        $currentMultiplier = round(1 + (($crash - 1) * $progress), 2);
    } elseif ($status === 'crashed') {
        $currentMultiplier = $crash;
    }
    return [
        'round_id' => (string) ($round['round_id'] ?? ''),
        'status' => $status,
        'server_time' => $now,
        'generated_at' => (int) ($round['generated_at'] ?? $now),
        'starts_at' => $startsAt,
        'ends_at' => $endsAt,
        'duration_ms' => $durationMs,
        'seconds_left' => max(0, $startsAt - $now),
        'crash_value' => ($adminReveal || $now >= $startsAt) ? $crash : null,
        'current_multiplier' => $currentMultiplier,
        'history' => aviator_history(),
    ];
}

function aviator_json_response(array $payload, int $code = 200): void {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    echo json_encode($payload, JSON_UNESCAPED_SLASHES);
    exit;
}

function aviator_admin_ok(): bool {
    $pin = (string) ($_POST['pin'] ?? $_GET['pin'] ?? '');
    return hash_equals(AVIATOR_ADMIN_PIN, $pin);
}
