<?php

declare(strict_types=1);

namespace App\Domain\Database\Services;

use App\Domain\Database\Models\MysqlDatabase;
use App\Domain\Database\Models\MysqlUser;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use RuntimeException;

/**
 * phpMyAdmin Single Sign-On (SSO) — Roundcube-style imzalı token.
 *
 * Akış:
 *   1) Panel POST /panel/databases/{db}/pma-sso çağrılır.
 *   2) issue() bir token üretir, HMAC-SHA256 imzalar, Cache::put ile 5 dk TTL.
 *   3) Frontend dönen redirect_url'i window.open ile açar.
 *   4) Production phpMyAdmin tarafında /etc/phpmyadmin/onx-sso-plugin.php
 *      ?onx_sso=<token> parametresini yakalar, panel'in /api/pma-sso/consume
 *      uç noktasını çağırarak token'ı doğrular ve $_SESSION'a yerleştirir.
 *
 * NOT (production-side): phpMyAdmin paketine ayrı bir auth plugin yazılması
 * gerekir (signon authentication type kullanılır). Bu sınıf panel tarafının
 * yarısını sağlar; phpMyAdmin tarafı operasyon dokümanına ekli.
 */
final class PhpMyAdminSso
{
    /** Token TTL — 5 dakika (cPanel parity). */
    public const TTL_SECONDS = 300;

    /** Cache anahtarı prefix'i. */
    private const CACHE_PREFIX = 'pma_sso:';

    /**
     * Yeni bir imzalı SSO token üret.
     *
     * @return array{token:string,redirect_url:string,expires_at:string,db_name:string,db_user:string}
     */
    public function issue(MysqlDatabase $database, ?MysqlUser $user = null): array
    {
        // Eğer kullanıcı verilmediyse, DB'ye yetkili ilk kullanıcı seçilir
        $dbUser = $user?->real_user_name
            ?? $database->users()->first()?->real_user_name
            ?? throw new RuntimeException(
                "phpMyAdmin SSO için kullanıcı bulunamadı: {$database->real_db_name}. ".
                'Önce bir DB kullanıcısı oluşturup yetki atayın.'
            );

        $token = Str::random(48);
        $payload = [
            'db_name' => $database->real_db_name,
            'db_user' => $dbUser,
            'account_id' => $database->account_id,
            'issued_at' => now()->toIso8601String(),
            'expires_at' => now()->addSeconds(self::TTL_SECONDS)->toIso8601String(),
            'signature' => $this->sign($token, $database->real_db_name, $dbUser),
        ];

        Cache::put(self::CACHE_PREFIX.$token, $payload, self::TTL_SECONDS);

        return [
            'token' => $token,
            'redirect_url' => $this->buildRedirectUrl($token),
            'expires_at' => $payload['expires_at'],
            'db_name' => $database->real_db_name,
            'db_user' => $dbUser,
        ];
    }

    /**
     * Admin için root SSO — tüm DB'lere root erişimi.
     *
     * @return array{token:string,redirect_url:string,expires_at:string,db_name:string,db_user:string}
     */
    public function issueForAdmin(string $dbName = ''): array
    {
        $token = Str::random(48);
        $payload = [
            'db_name' => $dbName,
            'db_user' => 'root',
            'is_admin' => true,
            'issued_at' => now()->toIso8601String(),
            'expires_at' => now()->addSeconds(self::TTL_SECONDS)->toIso8601String(),
            'signature' => $this->sign($token, $dbName, 'root'),
        ];

        Cache::put(self::CACHE_PREFIX.$token, $payload, self::TTL_SECONDS);

        return [
            'token' => $token,
            'redirect_url' => $this->buildRedirectUrl($token, $dbName),
            'expires_at' => $payload['expires_at'],
            'db_name' => $dbName,
            'db_user' => 'root',
        ];
    }

    /**
     * Token'ı tüket — phpMyAdmin tarafındaki signon plugin çağırır.
     *
     * @return array<string, mixed>|null Geçersiz veya süresi dolmuşsa null.
     */
    public function consume(string $token): ?array
    {
        $key = self::CACHE_PREFIX.$token;
        $payload = Cache::get($key);

        if (! is_array($payload)) {
            return null;
        }

        $expected = $this->sign($token, (string) ($payload['db_name'] ?? ''), (string) ($payload['db_user'] ?? ''));
        if (! hash_equals($expected, (string) ($payload['signature'] ?? ''))) {
            return null;
        }

        Cache::forget($key); // tek seferlik (one-shot) token

        return $payload;
    }

    /**
     * Doğrulanmış redirect URL'i oluştur — phpMyAdmin index.php?onx_sso=...
     */
    public function buildRedirectUrl(string $token, string $dbName = ''): string
    {
        $base = rtrim((string) config('onoxsoft.phpmyadmin_url', '/phpmyadmin/'), '/');
        $url = $base.'/index.php?onx_sso='.urlencode($token);
        if ($dbName !== '') {
            $url .= '&db='.urlencode($dbName);
        }

        return $url;
    }

    /**
     * HMAC-SHA256 imza — APP_KEY tabanlı.
     */
    private function sign(string $token, string $dbName, string $dbUser): string
    {
        $secret = (string) config('app.key', '');
        $data = $token.'|'.$dbName.'|'.$dbUser;

        return hash_hmac('sha256', $data, $secret);
    }
}
