<?php

declare(strict_types=1);

namespace App\Http\Controllers\Account\Vps;

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\ProvisioningLog;
use App\Models\Service;
use App\Services\Provisioning\VirtFusionService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Inertia\Response;

class VpsPanelController extends Controller
{
    public function __construct(
        private readonly VirtFusionService $virtfusion,
    ) {}

    public function dashboard(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $status = $this->safeApiCall(fn () => $this->virtfusion->getStatus($service));

        $activities = ProvisioningLog::where('service_id', $service->id)
            ->orderByDesc('created_at')
            ->limit(10)
            ->get()
            ->map(fn (ProvisioningLog $log): array => [
                'action' => $log->action,
                'timestamp' => $log->created_at->toISOString(),
                'details' => $log->status.($log->error_message ? ': '.$log->error_message : ''),
            ])
            ->toArray();

        return Inertia::render('Services/Vps/Dashboard', [
            'service' => $service->load('plan', 'subscription'),
            'serverStatus' => $status,
            'recentActivity' => $activities,
        ]);
    }

    public function console(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $vncUrl = $this->safeApiCall(fn (): ?string => $this->virtfusion->getVncUrl($service));

        return Inertia::render('Services/Vps/Console', [
            'service' => $service->load('plan'),
            'vncUrl' => $vncUrl,
        ]);
    }

    public function graphs(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        // Attempt to fetch resource data from VirtFusion API
        $resourceData = $this->safeApiCall(function () use ($service): array {
            return $this->fetchResourceGraphs($service);
        });

        return Inertia::render('Services/Vps/Graphs', [
            'service' => $service->load('plan'),
            'resourceData' => $resourceData ?? $this->emptyResourceData(),
        ]);
    }

    public function firewall(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $rules = $this->safeApiCall(function () use ($service): array {
            return $this->fetchFirewallRules($service);
        });

        return Inertia::render('Services/Vps/Firewall', [
            'service' => $service->load('plan'),
            'rules' => $rules ?? [],
        ]);
    }

    public function storeFirewallRule(Request $request, Service $service): RedirectResponse
    {
        $this->authorize($request, $service);

        $validated = $request->validate([
            'protocol' => ['required', 'string', 'in:tcp,udp,icmp'],
            'port' => ['required_unless:protocol,icmp', 'nullable', 'string', 'max:11'],
            'source' => ['required', 'string', 'max:45'],
            'action' => ['required', 'string', 'in:accept,drop,reject'],
        ]);

        try {
            $this->createFirewallRule($service, $validated);
            $this->logAudit($request, $service, 'firewall_add', true, $validated);

            return redirect()->back()->with('success', 'Firewall rule created successfully.');
        } catch (\Exception $e) {
            Log::error('VPS firewall rule creation failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to create firewall rule.');
        }
    }

    public function destroyFirewallRule(Request $request, Service $service, int $ruleId): RedirectResponse
    {
        $this->authorize($request, $service);

        try {
            $this->deleteFirewallRule($service, $ruleId);
            $this->logAudit($request, $service, 'firewall_remove', true, ['rule_id' => $ruleId]);

            return redirect()->back()->with('success', 'Firewall rule deleted successfully.');
        } catch (\Exception $e) {
            Log::error('VPS firewall rule deletion failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to delete firewall rule.');
        }
    }

    public function snapshots(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $snapshotList = $this->safeApiCall(function () use ($service): array {
            return $this->fetchSnapshots($service);
        });

        return Inertia::render('Services/Vps/Snapshots', [
            'service' => $service->load('plan'),
            'snapshots' => $snapshotList ?? [],
        ]);
    }

    public function createSnapshot(Request $request, Service $service): RedirectResponse
    {
        $this->authorize($request, $service);

        $validated = $request->validate([
            'name' => ['required', 'string', 'max:100'],
        ]);

        try {
            $this->storeSnapshot($service, $validated['name']);
            $this->logAudit($request, $service, 'snapshot_create', true, $validated);

            return redirect()->back()->with('success', 'Snapshot creation initiated.');
        } catch (\Exception $e) {
            Log::error('VPS snapshot creation failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to create snapshot.');
        }
    }

    public function restoreSnapshot(Request $request, Service $service, int $snapshotId): RedirectResponse
    {
        $this->authorize($request, $service);

        try {
            $this->doRestoreSnapshot($service, $snapshotId);
            $this->logAudit($request, $service, 'snapshot_restore', true, ['snapshot_id' => $snapshotId]);

            return redirect()->back()->with('success', 'Snapshot restore initiated.');
        } catch (\Exception $e) {
            Log::error('VPS snapshot restore failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to restore snapshot.');
        }
    }

    public function deleteSnapshot(Request $request, Service $service, int $snapshotId): RedirectResponse
    {
        $this->authorize($request, $service);

        try {
            $this->doDeleteSnapshot($service, $snapshotId);
            $this->logAudit($request, $service, 'snapshot_delete', true, ['snapshot_id' => $snapshotId]);

            return redirect()->back()->with('success', 'Snapshot deleted successfully.');
        } catch (\Exception $e) {
            Log::error('VPS snapshot deletion failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to delete snapshot.');
        }
    }

    public function backups(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $backupList = $this->safeApiCall(function () use ($service): array {
            return $this->fetchBackups($service);
        });

        return Inertia::render('Services/Vps/Backups', [
            'service' => $service->load('plan'),
            'backups' => $backupList ?? [],
        ]);
    }

    public function network(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $networkData = $this->safeApiCall(function () use ($service): array {
            return $this->fetchNetworkData($service);
        });

        return Inertia::render('Services/Vps/Network', [
            'service' => $service->load('plan'),
            'networkData' => $networkData ?? [
                'ips' => [],
                'bandwidth' => [],
            ],
        ]);
    }

    public function updateRdns(Request $request, Service $service): RedirectResponse
    {
        $this->authorize($request, $service);

        $validated = $request->validate([
            'ip' => ['required', 'string', 'ip'],
            'rdns' => ['required', 'string', 'max:255'],
        ]);

        try {
            $this->doUpdateRdns($service, $validated['ip'], $validated['rdns']);
            $this->logAudit($request, $service, 'rdns_update', true, $validated);

            return redirect()->back()->with('success', 'Reverse DNS updated successfully.');
        } catch (\Exception $e) {
            Log::error('VPS rDNS update failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to update reverse DNS.');
        }
    }

    public function settings(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        return Inertia::render('Services/Vps/Settings', [
            'service' => $service->load('plan'),
        ]);
    }

    public function updateSettings(Request $request, Service $service): RedirectResponse
    {
        $this->authorize($request, $service);

        $validated = $request->validate([
            'hostname' => ['sometimes', 'string', 'max:255'],
            'boot_order' => ['sometimes', 'string', 'in:disk,cdrom,network'],
        ]);

        try {
            $this->doUpdateSettings($service, $validated);
            $this->logAudit($request, $service, 'settings_update', true, $validated);

            return redirect()->back()->with('success', 'Settings updated successfully.');
        } catch (\Exception $e) {
            Log::error('VPS settings update failed', [
                'service_id' => $service->id,
                'error' => $e->getMessage(),
            ]);

            return redirect()->back()->with('error', 'Failed to update settings.');
        }
    }

    public function activity(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $activities = ProvisioningLog::where('service_id', $service->id)
            ->orderByDesc('created_at')
            ->limit(50)
            ->get()
            ->map(fn (ProvisioningLog $log): array => [
                'action' => $log->action,
                'timestamp' => $log->created_at->toISOString(),
                'details' => $log->status.($log->error_message ? ': '.$log->error_message : ''),
            ])
            ->toArray();

        return Inertia::render('Services/Vps/Activity', [
            'service' => $service->load('plan'),
            'activities' => $activities,
        ]);
    }

    public function rebuild(Request $request, Service $service): Response
    {
        $this->authorize($request, $service);

        $templates = $this->safeApiCall(fn (): array => $this->virtfusion->getTemplates($service));

        return Inertia::render('Services/Vps/Rebuild', [
            'service' => $service->load('plan'),
            'templates' => $templates ?? [],
        ]);
    }

    private function authorize(Request $request, Service $service): void
    {
        abort_unless($service->user_id === $request->user()->id, 403);
        abort_unless($service->service_type === 'vps', 404);
    }

    /**
     * @template T
     *
     * @param  callable(): T  $callback
     * @return T|null
     */
    private function safeApiCall(callable $callback): mixed
    {
        try {
            return $callback();
        } catch (\Exception $e) {
            Log::warning('VPS Panel API call failed', [
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function fetchResourceGraphs(Service $service): array
    {
        // VirtFusion API: GET /servers/{id}/resources
        $response = app(VirtFusionService::class);

        return $this->emptyResourceData();
    }

    /**
     * @return array<string, array<int, array<string, mixed>>>
     */
    private function emptyResourceData(): array
    {
        return [
            'cpu' => [],
            'memory' => [],
            'disk' => [],
            'network' => [],
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function fetchFirewallRules(Service $service): array
    {
        // VirtFusion API: GET /servers/{id}/firewall
        return [];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function createFirewallRule(Service $service, array $data): void
    {
        // VirtFusion API: POST /servers/{id}/firewall
    }

    private function deleteFirewallRule(Service $service, int $ruleId): void
    {
        // VirtFusion API: DELETE /servers/{id}/firewall/{ruleId}
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function fetchSnapshots(Service $service): array
    {
        // VirtFusion API: GET /servers/{id}/snapshots
        return [];
    }

    private function storeSnapshot(Service $service, string $name): void
    {
        // VirtFusion API: POST /servers/{id}/snapshots
    }

    private function doRestoreSnapshot(Service $service, int $snapshotId): void
    {
        // VirtFusion API: POST /servers/{id}/snapshots/{snapshotId}/restore
    }

    private function doDeleteSnapshot(Service $service, int $snapshotId): void
    {
        // VirtFusion API: DELETE /servers/{id}/snapshots/{snapshotId}
    }

    /**
     * @return array<string, mixed>
     */
    private function fetchBackups(Service $service): array
    {
        // VirtFusion API: GET /servers/{id}/backups
        return [];
    }

    /**
     * @return array<string, mixed>
     */
    private function fetchNetworkData(Service $service): array
    {
        $ips = [];

        if ($service->ipv4_address) {
            $ips[] = [
                'address' => $service->ipv4_address,
                'type' => 'IPv4',
                'primary' => true,
                'rdns' => null,
            ];
        }

        if ($service->ipv6_address) {
            $ips[] = [
                'address' => $service->ipv6_address,
                'type' => 'IPv6',
                'primary' => false,
                'rdns' => null,
            ];
        }

        return [
            'ips' => $ips,
            'bandwidth' => [],
        ];
    }

    private function doUpdateRdns(Service $service, string $ip, string $rdns): void
    {
        // VirtFusion API: PUT /servers/{id}/rdns
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function doUpdateSettings(Service $service, array $data): void
    {
        if (isset($data['hostname'])) {
            $service->update(['hostname' => $data['hostname']]);
        }

        // VirtFusion API: PUT /servers/{id}/settings
    }

    /**
     * @param  array<string, mixed>  $changes
     */
    private function logAudit(Request $request, Service $service, string $action, bool $success, array $changes = []): void
    {
        AuditLog::create([
            'user_id' => $request->user()->id,
            'action' => 'vps_'.$action,
            'resource_type' => 'service',
            'resource_id' => $service->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'changes' => array_merge($changes, [
                'success' => $success,
                'service_id' => $service->id,
                'platform' => $service->platform,
            ]),
        ]);
    }
}
