<?php

declare(strict_types=1);

namespace App\Domain\Database\Services;

use App\Domain\Sysapi\Contracts\SysapiBridge;
use App\Domain\Sysapi\Exceptions\SysapiException;
use Illuminate\Support\Facades\Log;

/**
 * MariaDB yönetim operasyonları — admin/WHM katmanı.
 *
 * Müşteri DB/kullanıcı işlemleri için DatabaseProvisioner kullanın.
 * Bu sınıf sunucu genelinde yönetim komutlarını (flush, optimize, repair)
 * sysapi aracılığıyla çalıştırır.
 *
 * Master plan §5 — Sysapi katmanı.
 */
final class MysqlProvisioner
{
    public function __construct(private readonly SysapiBridge $sysapi) {}

    // ─── Customer DB + User ───────────────────────────────────────────────────

    /**
     * @return array{db_name:string,created:bool,charset:string,size_kb:int}
     *
     * @throws SysapiException
     */
    public function createDatabase(
        string $accountUsername,
        string $dbName,
        string $charset = 'utf8mb4',
        string $collation = 'utf8mb4_unicode_ci',
    ): array {
        try {
            $result = $this->sysapi->run('db-create', [
                'db_name' => $dbName,
                'charset' => $charset,
                'collation' => $collation,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::createDatabase failed', [
                    'db_name' => $dbName,
                    'stderr' => $result->stderr,
                    'exit' => $result->exitCode,
                ]);
                throw SysapiException::fromFailedResult('db-create', $result);
            }

            return $result->data ?? ['db_name' => $dbName, 'created' => true, 'charset' => $charset, 'size_kb' => 0];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::createDatabase SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @return array{db_name:string,dropped:bool}
     *
     * @throws SysapiException
     */
    public function dropDatabase(string $dbName): array
    {
        try {
            $result = $this->sysapi->run('db-drop', ['db_name' => $dbName]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::dropDatabase failed', [
                    'db_name' => $dbName,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-drop', $result);
            }

            return $result->data ?? ['db_name' => $dbName, 'dropped' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::dropDatabase SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @return array{db_user:string,host:string,created:bool}
     *
     * @throws SysapiException
     */
    public function createUser(
        string $dbUser,
        string $password,
        string $host = 'localhost',
    ): array {
        try {
            $result = $this->sysapi->run('db-user-create', [
                'db_user' => $dbUser,
                'password' => $password,
                'host' => $host,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::createUser failed', [
                    'db_user' => $dbUser,
                    'host' => $host,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-user-create', $result);
            }

            return $result->data ?? ['db_user' => $dbUser, 'host' => $host, 'created' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::createUser SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @return array{db_user:string,host:string,dropped:bool}
     *
     * @throws SysapiException
     */
    public function dropUser(string $dbUser, string $host): array
    {
        try {
            $result = $this->sysapi->run('db-user-drop', [
                'db_user' => $dbUser,
                'host' => $host,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::dropUser failed', [
                    'db_user' => $dbUser,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-user-drop', $result);
            }

            return $result->data ?? ['db_user' => $dbUser, 'host' => $host, 'dropped' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::dropUser SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @return array{db_user:string,host:string,updated:bool}
     *
     * @throws SysapiException
     */
    public function resetUserPassword(string $dbUser, string $host, string $password): array
    {
        try {
            $result = $this->sysapi->run('db-user-password', [
                'db_user' => $dbUser,
                'host' => $host,
                'password' => $password,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::resetUserPassword failed', [
                    'db_user' => $dbUser,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-user-password', $result);
            }

            return $result->data ?? ['db_user' => $dbUser, 'host' => $host, 'updated' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::resetUserPassword SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @param  array<int,string>  $privileges
     * @return array{db_user:string,db_name:string,granted:bool}
     *
     * @throws SysapiException
     */
    public function grant(string $dbUser, string $host, string $dbName, array $privileges = ['ALL']): array
    {
        try {
            $result = $this->sysapi->run('db-grant', [
                'db_user' => $dbUser,
                'host' => $host,
                'db_name' => $dbName,
                'privileges' => $privileges,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::grant failed', [
                    'db_user' => $dbUser,
                    'db_name' => $dbName,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-grant', $result);
            }

            return $result->data ?? ['db_user' => $dbUser, 'db_name' => $dbName, 'granted' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::grant SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * @param  array<int,string>  $privileges
     * @return array{db_user:string,db_name:string,revoked:bool}
     *
     * @throws SysapiException
     */
    public function revoke(string $dbUser, string $host, string $dbName, array $privileges = ['ALL']): array
    {
        try {
            $result = $this->sysapi->run('db-revoke', [
                'db_user' => $dbUser,
                'host' => $host,
                'db_name' => $dbName,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::revoke failed', [
                    'db_user' => $dbUser,
                    'db_name' => $dbName,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-revoke', $result);
            }

            return $result->data ?? ['db_user' => $dbUser, 'db_name' => $dbName, 'revoked' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::revoke SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    // ─── Admin operasyonları ──────────────────────────────────────────────────

    /**
     * FLUSH HOSTS — bağlantı hataları sıfırlanır.
     *
     * @return array{flushed:bool,operation:string}
     *
     * @throws SysapiException
     */
    public function flushHosts(): array
    {
        try {
            $result = $this->sysapi->run('db-flush-hosts', []);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::flushHosts failed', ['stderr' => $result->stderr]);
                throw SysapiException::fromFailedResult('db-flush-hosts', $result);
            }

            return $result->data ?? ['flushed' => true, 'operation' => 'FLUSH HOSTS'];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::flushHosts SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * FLUSH QUERY CACHE — sorgu önbelleği temizlenir.
     *
     * @return array{flushed:bool,operation:string}
     *
     * @throws SysapiException
     */
    public function flushQueryCache(): array
    {
        try {
            $result = $this->sysapi->run('db-flush-query-cache', []);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::flushQueryCache failed', ['stderr' => $result->stderr]);
                throw SysapiException::fromFailedResult('db-flush-query-cache', $result);
            }

            return $result->data ?? ['flushed' => true, 'operation' => 'FLUSH QUERY CACHE'];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::flushQueryCache SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * mysqlcheck --optimize
     *
     * @return array{db_name:string,table:string|null,optimized:bool}
     *
     * @throws SysapiException
     */
    public function optimizeTable(string $dbName, ?string $table = null): array
    {
        try {
            $args = ['db_name' => $dbName];
            if ($table !== null) {
                $args['table'] = $table;
            }

            $result = $this->sysapi->run('db-optimize-table', $args);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::optimizeTable failed', [
                    'db_name' => $dbName,
                    'table' => $table,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-optimize-table', $result);
            }

            return $result->data ?? ['db_name' => $dbName, 'table' => $table, 'optimized' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::optimizeTable SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }

    /**
     * mysqlcheck --repair
     *
     * @return array{db_name:string,table:string,repaired:bool}
     *
     * @throws SysapiException
     */
    public function repairTable(string $dbName, string $table): array
    {
        try {
            $result = $this->sysapi->run('db-repair-table', [
                'db_name' => $dbName,
                'table' => $table,
            ]);

            if ($result->failed()) {
                Log::error('MysqlProvisioner::repairTable failed', [
                    'db_name' => $dbName,
                    'table' => $table,
                    'stderr' => $result->stderr,
                ]);
                throw SysapiException::fromFailedResult('db-repair-table', $result);
            }

            return $result->data ?? ['db_name' => $dbName, 'table' => $table, 'repaired' => true];
        } catch (SysapiException $e) {
            Log::error('MysqlProvisioner::repairTable SysapiException', ['message' => $e->getMessage()]);
            throw $e;
        }
    }
}
