<?php

declare(strict_types=1);

namespace App\Domain\Replication\Services;

use App\Domain\Cluster\Models\ClusterNode;
use App\Domain\Replication\Models\ReplicationSetup;
use App\Domain\Sysapi\Contracts\SysapiBridge;
use Illuminate\Support\Str;

class MariadbReplicationManager
{
    public function __construct(private readonly SysapiBridge $sysapi) {}

    /**
     * Master-slave kurulumu başlatır.
     * Sıra:
     *   1) Replication kullanıcısı oluştur (rastgele şifre)
     *   2) Master'ı yapılandır (log_bin, server-id, binlog_format)
     *   3) Her slave için slave setup sysapi çağrısı
     *   4) Slave durumlarını doğrula
     *   5) replication_setups kaydı oluştur / güncelle
     *
     * @param  int[]  $slaveIds  ClusterNode id listesi
     */
    public function configureMasterSlave(ClusterNode $master, array $slaveIds): array
    {
        $replUser = 'replicator';
        $replPassword = Str::random(32);

        // 1 & 2 — Master kurulumu
        $masterResult = $this->sysapi->run('mariadb-master-setup', [
            'repl_user' => $replUser,
            'repl_password' => $replPassword,
            'server_id' => 1,
        ], timeoutSeconds: 120);

        if ($masterResult->failed()) {
            return ['success' => false, 'error' => 'Master setup başarısız: '.$masterResult->stderr, 'step' => 'master'];
        }

        $logFile = $masterResult->data['log_file'] ?? '';
        $logPos = $masterResult->data['log_pos'] ?? 0;

        // 3 — Her slave'i kur
        $slaveResults = [];
        $serverId = 2;
        foreach ($slaveIds as $slaveId) {
            $slave = ClusterNode::findOrFail($slaveId);

            $slaveResult = $this->sysapi->run('mariadb-slave-setup', [
                'master_host' => $master->ip_address,
                'master_port' => 3306,
                'repl_user' => $replUser,
                'repl_password' => $replPassword,
                'log_file' => $logFile,
                'log_pos' => $logPos,
                'server_id' => $serverId++,
            ], timeoutSeconds: 180);

            $slaveResults[$slaveId] = [
                'node_id' => $slaveId,
                'hostname' => $slave->hostname,
                'success' => ! $slaveResult->failed(),
                'data' => $slaveResult->data,
                'error' => $slaveResult->failed() ? $slaveResult->stderr : null,
            ];
        }

        // 4 — Kayıt oluştur
        $setup = ReplicationSetup::create([
            'service_type' => 'mariadb',
            'topology' => 'master-slave',
            'master_node_id' => $master->id,
            'slave_node_ids' => $slaveIds,
            'status' => collect($slaveResults)->every(fn ($r) => $r['success']) ? 'active' : 'degraded',
            'config_snapshot' => [
                'repl_user' => $replUser,
                'log_file' => $logFile,
                'log_pos' => $logPos,
                'setup_at' => now()->toIso8601String(),
            ],
            'created_by' => auth()->id(),
        ]);

        return [
            'success' => true,
            'setup_id' => $setup->id,
            'master' => $masterResult->data,
            'slaves' => $slaveResults,
        ];
    }

    /**
     * Her slave için replication lag bilgisini döner.
     */
    public function checkLag(ReplicationSetup $setup): array
    {
        $slaves = [];
        foreach ($setup->slave_node_ids ?? [] as $slaveId) {
            $result = $this->sysapi->run('mariadb-slave-status', [], timeoutSeconds: 30);
            $slaves[$slaveId] = array_merge(
                ['node_id' => $slaveId],
                $result->failed() ? ['error' => $result->stderr, 'seconds_behind_master' => null] : $result->data
            );
        }

        return [
            'checked_at' => now()->toIso8601String(),
            'slaves' => $slaves,
        ];
    }

    /**
     * Slave'i master'a yükselt (failover).
     * - STOP SLAVE → RESET MASTER → read_only kaldır
     */
    public function promoteSlave(ClusterNode $slave): array
    {
        $result = $this->sysapi->run('mariadb-promote', [], timeoutSeconds: 60);

        return [
            'success' => ! $result->failed(),
            'node_id' => $slave->id,
            'data' => $result->data,
            'error' => $result->failed() ? $result->stderr : null,
        ];
    }

    /**
     * Slave üzerindeki replikasyonu durdur.
     */
    public function pauseReplication(ReplicationSetup $setup): array
    {
        $results = [];
        foreach ($setup->slave_node_ids ?? [] as $slaveId) {
            $result = $this->sysapi->run('mariadb-slave-status', ['action' => 'pause'], timeoutSeconds: 30);
            $results[$slaveId] = ['success' => ! $result->failed()];
        }

        $setup->update(['status' => 'paused']);

        return ['success' => true, 'slaves' => $results];
    }

    /**
     * Slave üzerindeki replikasyonu sürdür.
     */
    public function resumeReplication(ReplicationSetup $setup): array
    {
        $results = [];
        foreach ($setup->slave_node_ids ?? [] as $slaveId) {
            $result = $this->sysapi->run('mariadb-slave-status', ['action' => 'resume'], timeoutSeconds: 30);
            $results[$slaveId] = ['success' => ! $result->failed()];
        }

        $setup->update(['status' => 'active']);

        return ['success' => true, 'slaves' => $results];
    }

    /**
     * Slave'i sıfırdan master'dan yeniden senkronize eder (mariabackup).
     */
    public function rebuildSlave(ClusterNode $slave): array
    {
        $result = $this->sysapi->run('mariadb-rebuild', [
            'master_host' => $slave->ip_address,
        ], timeoutSeconds: 3600);

        return [
            'success' => ! $result->failed(),
            'node_id' => $slave->id,
            'data' => $result->data,
            'error' => $result->failed() ? $result->stderr : null,
        ];
    }
}
