<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Domain\Cluster\Models\ClusterNode;
use App\Domain\Replication\Events\ReplicationLagDetected;
use App\Domain\Replication\Models\ReplicationFailoverEvent;
use App\Domain\Replication\Models\ReplicationSetup;
use App\Domain\Replication\Services\MariadbReplicationManager;
use App\Domain\Replication\Services\PowerdnsReplicationManager;
use App\Domain\Replication\Services\RedisReplicationManager;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

class ReplicationController extends Controller
{
    // ─── Index ────────────────────────────────────────────────────────────────

    public function index(): Response
    {
        $setups = ReplicationSetup::with(['masterNode'])->latest()->get();

        $grouped = [
            'mariadb' => $setups->where('service_type', 'mariadb')->values(),
            'powerdns' => $setups->where('service_type', 'powerdns')->values(),
            'redis' => $setups->where('service_type', 'redis')->values(),
        ];

        foreach (['mariadb', 'powerdns', 'redis'] as $type) {
            $grouped[$type] = $grouped[$type]->map(fn ($s) => $this->formatSetup($s));
        }

        return Inertia::render('Admin/Replication/Index', [
            'grouped' => $grouped,
        ]);
    }

    // ─── Create / Wizard ──────────────────────────────────────────────────────

    public function create(): Response
    {
        $nodes = ClusterNode::whereNull('deleted_at')
            ->where('status', 'active')
            ->get()
            ->map(fn ($n) => [
                'id' => $n->id,
                'name' => $n->name,
                'hostname' => $n->hostname,
                'ip_address' => $n->ip_address,
                'role' => $n->role,
            ]);

        return Inertia::render('Admin/Replication/Wizard', [
            'nodes' => $nodes,
        ]);
    }

    // ─── Store ────────────────────────────────────────────────────────────────

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'service_type' => ['required', 'in:mariadb,powerdns,redis'],
            'master_node_id' => ['required', 'integer', 'exists:cluster_nodes,id'],
            'slave_node_ids' => ['required', 'array', 'min:1'],
            'slave_node_ids.*' => ['integer', 'exists:cluster_nodes,id'],
            'sentinel_node_ids' => ['nullable', 'array'],
            'sentinel_node_ids.*' => ['integer', 'exists:cluster_nodes,id'],
        ]);

        $master = ClusterNode::findOrFail($validated['master_node_id']);
        $slaveIds = $validated['slave_node_ids'];

        $result = match ($validated['service_type']) {
            'mariadb' => app(MariadbReplicationManager::class)->configureMasterSlave($master, $slaveIds),
            'powerdns' => app(PowerdnsReplicationManager::class)->configureMasterSlave($master, $slaveIds),
            'redis' => app(RedisReplicationManager::class)->configureSentinel(
                $master,
                $slaveIds,
                $validated['sentinel_node_ids'] ?? $slaveIds
            ),
        };

        if (! ($result['success'] ?? false)) {
            return back()->with('error', 'Replication kurulumu başarısız: '.($result['error'] ?? 'Bilinmeyen hata'));
        }

        return redirect()->route('admin.replication.show', $result['setup_id'])
            ->with('success', 'Replication kurulumu başlatıldı.');
    }

    // ─── Show ─────────────────────────────────────────────────────────────────

    public function show(ReplicationSetup $setup): Response
    {
        $setup->load(['masterNode']);
        $slaveNodes = $setup->slaveNodes()->get()->map(fn ($n) => [
            'id' => $n->id,
            'name' => $n->name,
            'hostname' => $n->hostname,
            'ip_address' => $n->ip_address,
        ]);

        $events = $setup->failoverEvents()
            ->with(['previousMaster', 'newMaster'])
            ->latest('occurred_at')
            ->limit(20)
            ->get()
            ->map(fn ($e) => [
                'id' => $e->id,
                'event_type' => $e->event_type,
                'automatic' => $e->automatic,
                'details' => $e->details,
                'previous_master' => $e->previousMaster?->name,
                'new_master' => $e->newMaster?->name,
                'occurred_at' => $e->occurred_at->format('d.m.Y H:i:s'),
            ]);

        return Inertia::render('Admin/Replication/Show', [
            'setup' => $this->formatSetup($setup),
            'slave_nodes' => $slaveNodes,
            'events' => $events,
        ]);
    }

    // ─── Pause ────────────────────────────────────────────────────────────────

    public function pause(ReplicationSetup $setup): RedirectResponse
    {
        if ($setup->status === 'paused') {
            return back()->with('error', 'Replication zaten duraklatılmış.');
        }

        $manager = $this->resolveManager($setup->service_type);
        if (method_exists($manager, 'pauseReplication')) {
            $manager->pauseReplication($setup);
        }
        $setup->update(['status' => 'paused']);

        return back()->with('success', 'Replication duraklatıldı.');
    }

    // ─── Resume ───────────────────────────────────────────────────────────────

    public function resume(ReplicationSetup $setup): RedirectResponse
    {
        if ($setup->status !== 'paused') {
            return back()->with('error', 'Replication duraklatılmış değil.');
        }

        $manager = $this->resolveManager($setup->service_type);
        if (method_exists($manager, 'resumeReplication')) {
            $manager->resumeReplication($setup);
        }
        $setup->update(['status' => 'active']);

        return back()->with('success', 'Replication sürdürüldü.');
    }

    // ─── Promote Slave (Manuel Failover) ─────────────────────────────────────

    public function promoteSlave(ReplicationSetup $setup, ClusterNode $slave): RedirectResponse
    {
        $manager = $this->resolveManager($setup->service_type);

        if (! method_exists($manager, 'promoteSlave')) {
            return back()->with('error', 'Bu servis türü için promote desteklenmiyor.');
        }

        $result = $manager->promoteSlave($slave);

        ReplicationFailoverEvent::create([
            'replication_setup_id' => $setup->id,
            'event_type' => 'manual_failover',
            'previous_master_id' => $setup->master_node_id,
            'new_master_id' => $slave->id,
            'details' => $result,
            'automatic' => false,
            'occurred_at' => now(),
        ]);

        if ($result['success'] ?? false) {
            $setup->update([
                'master_node_id' => $slave->id,
                'slave_node_ids' => array_filter($setup->slave_node_ids, fn ($id) => $id !== $slave->id),
                'status' => 'active',
            ]);

            return back()->with('success', "Node '{$slave->name}' yeni master olarak tanımlandı.");
        }

        return back()->with('error', 'Promote başarısız: '.($result['error'] ?? 'Bilinmeyen hata'));
    }

    // ─── Health Check ─────────────────────────────────────────────────────────

    public function healthCheck(ReplicationSetup $setup): RedirectResponse
    {
        $manager = $this->resolveManager($setup->service_type);
        $health = $manager->checkLag($setup);

        $setup->update([
            'health_status' => $health,
            'last_health_check_at' => now(),
        ]);

        // Lag > 30s → event kaydet
        foreach ($health['slaves'] ?? [] as $slaveData) {
            if (($slaveData['seconds_behind_master'] ?? 0) > 30) {
                DB::table('replication_failover_events')->insert([
                    'replication_setup_id' => $setup->id,
                    'event_type' => 'lag_detected',
                    'details' => json_encode($slaveData),
                    'automatic' => true,
                    'occurred_at' => now(),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
                event(new ReplicationLagDetected($setup, $slaveData));
            }
        }

        return back()->with('success', 'Sağlık kontrolü tamamlandı.');
    }

    // ─── Delete ───────────────────────────────────────────────────────────────

    public function delete(ReplicationSetup $setup): RedirectResponse
    {
        $setup->delete();

        return redirect()->route('admin.replication.index')
            ->with('success', 'Replication konfigürasyonu silindi.');
    }

    // ─── Private Helpers ──────────────────────────────────────────────────────

    private function formatSetup(ReplicationSetup $setup): array
    {
        return [
            'id' => $setup->id,
            'service_type' => $setup->service_type,
            'topology' => $setup->topology,
            'status' => $setup->status,
            'master_node_id' => $setup->master_node_id,
            'master_node_name' => $setup->masterNode?->name,
            'master_node_hostname' => $setup->masterNode?->hostname,
            'slave_node_ids' => $setup->slave_node_ids,
            'health_status' => $setup->health_status,
            'last_health_check_at' => $setup->last_health_check_at?->diffForHumans(),
            'created_at' => $setup->created_at->format('d.m.Y H:i'),
        ];
    }

    private function resolveManager(string $serviceType): MariadbReplicationManager|PowerdnsReplicationManager|RedisReplicationManager
    {
        return match ($serviceType) {
            'mariadb' => app(MariadbReplicationManager::class),
            'powerdns' => app(PowerdnsReplicationManager::class),
            'redis' => app(RedisReplicationManager::class),
            default => throw new \InvalidArgumentException("Bilinmeyen servis türü: {$serviceType}"),
        };
    }
}
