<?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;

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

    /**
     * Redis Sentinel HA kurulumu.
     *
     * Topoloji:
     *   - 1 master + N replica (replicaof)
     *   - K sentinel süreci (quorum = floor(K/2)+1)
     *
     * /etc/redis/sentinel.conf üretilir ve sentinel servisi başlatılır.
     *
     * @param  int[]  $slaveIds  Replica node id'leri
     * @param  int[]  $sentinelIds  Sentinel süreci çalışacak node id'leri
     */
    public function configureSentinel(ClusterNode $master, array $slaveIds, array $sentinelIds): array
    {
        $quorum = (int) floor(count($sentinelIds) / 2) + 1;

        // 1 — Slave'leri kur (REPLICAOF master_ip 6379)
        $slaveResults = [];
        foreach ($slaveIds as $slaveId) {
            $slave = ClusterNode::findOrFail($slaveId);
            $result = $this->sysapi->run('redis-replicaof', [
                'master_host' => $master->ip_address,
                'master_port' => 6379,
            ], timeoutSeconds: 60);

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

        // 2 — Sentinel'ları kur
        $sentinelResults = [];
        foreach ($sentinelIds as $sentinelId) {
            $node = ClusterNode::findOrFail($sentinelId);
            $result = $this->sysapi->run('redis-sentinel-setup', [
                'master_name' => 'onox-master',
                'master_host' => $master->ip_address,
                'master_port' => 6379,
                'quorum' => $quorum,
            ], timeoutSeconds: 60);

            $sentinelResults[$sentinelId] = [
                'node_id' => $sentinelId,
                'hostname' => $node->hostname,
                'success' => ! $result->failed(),
                'data' => $result->data,
                'error' => $result->failed() ? $result->stderr : null,
            ];
        }

        $allOk = collect($slaveResults)->every(fn ($r) => $r['success'])
              && collect($sentinelResults)->every(fn ($r) => $r['success']);

        $allSlaveIds = array_merge($slaveIds, $sentinelIds);

        $setup = ReplicationSetup::create([
            'service_type' => 'redis',
            'topology' => 'sentinel',
            'master_node_id' => $master->id,
            'slave_node_ids' => $allSlaveIds,
            'status' => $allOk ? 'active' : 'degraded',
            'config_snapshot' => [
                'master_name' => 'onox-master',
                'quorum' => $quorum,
                'sentinel_ids' => $sentinelIds,
                'slave_ids' => $slaveIds,
                'setup_at' => now()->toIso8601String(),
            ],
            'created_by' => auth()->id(),
        ]);

        return [
            'success' => true,
            'setup_id' => $setup->id,
            'slaves' => $slaveResults,
            'sentinels' => $sentinelResults,
        ];
    }

    /**
     * Redis replikasyon durumunu kontrol eder.
     * checkLag uyumlu imza (ReplicationHealthCheckCommand ile aynı arayüz).
     */
    public function checkLag(ReplicationSetup $setup): array
    {
        $config = $setup->config_snapshot ?? [];
        $slaveIds = $config['slave_ids'] ?? $setup->slave_node_ids ?? [];

        $slaves = [];
        foreach ($slaveIds as $slaveId) {
            // Redis INFO replication komutu — sysapi üzerinden sorgu
            $result = $this->sysapi->run('redis-replicaof', [
                'action' => 'info',
            ], timeoutSeconds: 15);

            $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,
        ];
    }
}
