import { useEffect, useMemo, useRef, useState, Fragment } from 'react';
import {
    Monitor,
    Pencil,
    Plus,
    RefreshCw,
    Save,
    Search,
    Server,
    ShieldCheck,
    Trash2,
    X,
    Router,
    LockKeyhole,
    Network,
    Eye,
    EyeOff,
} from 'lucide-react';

import { Card } from '@/components/ui/Card';
import { Select } from '@/components/ui/Select';
import { vpsApi } from '@/services/vpsApi';
import { Button } from '@/components/ui/Button';

import type { ControllerClient } from '@/services/vpsApi';
import type { RouterFirewallRule } from '@/services/vpsApi';

const scrollClass =
    '[scrollbar-width:thin] [scrollbar-color:rgba(59,130,246,0.55)_transparent] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-blue-500/45 hover:[&::-webkit-scrollbar-thumb]:bg-blue-500/65';

const controllerFilterOptions: {
    value: 'all' | 'with' | 'without';
    label: string;
}[] = [
        { value: 'all', label: 'Todos' },
        { value: 'with', label: 'Com controladores' },
        { value: 'without', label: 'Sem controladores' },
    ];

const pageSizeOptions: {
    value: '10' | '25' | '50';
    label: string;
}[] = [
        { value: '10', label: '10 por página' },
        { value: '25', label: '25 por página' },
        { value: '50', label: '50 por página' },
    ];

export function ControllersRoute() {
    const [search, setSearch] = useState('');
    const [clients, setClients] = useState<ControllerClient[]>([]);
    const [selectedId, setSelectedId] = useState('');
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState('');
    const [page, setPage] = useState(1);
    const [pageSize, setPageSize] = useState<'10' | '25' | '50'>('10');
    const [controllerFilter, setControllerFilter] =
        useState<'all' | 'with' | 'without'>('all');

    const [editingClient, setEditingClient] =
        useState<ControllerClient | null>(null);

    const [creatingClient, setCreatingClient] = useState(false);
    const [deletingClientId, setDeletingClientId] = useState('');
    const [clientToDelete, setClientToDelete] = useState<ControllerClient | null>(null);
    const [selectedRouterIp, setSelectedRouterIp] = useState('');
    const [selectedClientFileContent, setSelectedClientFileContent] = useState('');
    const [loadingRouterData, setLoadingRouterData] = useState(false);
    const [routerDataError, setRouterDataError] = useState('');
    const [routerRules, setRouterRules] = useState<RouterFirewallRule[]>([]);
    const [routerPassword, setRouterPassword] = useState('litoralr');
    const [showRouterPassword, setShowRouterPassword] = useState(false);
    const [creatingRule, setCreatingRule] = useState(false);
    const [ruleToUpdate, setRuleToUpdate] = useState<RouterFirewallRule | null>(null);
    const [ruleToDelete, setRuleToDelete] = useState<RouterFirewallRule | null>(null);
    const [savingRule, setSavingRule] = useState(false);


    async function loadClients(selectClientId?: string) {
        try {
            setLoading(true);
            setError('');

            const response = await vpsApi.listControllerClients();

            setClients(response.clients);

            setSelectedId((current) =>
                selectClientId ||
                current ||
                response.clients[0]?.id ||
                '',
            );
        } catch (err) {
            setError(String(err));
            setClients([]);
            setSelectedId('');
        } finally {
            setLoading(false);
        }
    }

    useEffect(() => {
        loadClients();
    }, []);

    const filteredClients = useMemo(() => {
        return clients.filter((client) => {
            const matchesSearch = client.name
                .toLowerCase()
                .includes(search.toLowerCase());

            const matchesControllerFilter =
                controllerFilter === 'all' ||
                (controllerFilter === 'with' && client.controller_count > 0) ||
                (controllerFilter === 'without' && client.controller_count === 0);

            return matchesSearch && matchesControllerFilter;
        });
    }, [clients, search, controllerFilter]);

    useEffect(() => {
        setPage(1);
    }, [search, controllerFilter, pageSize]);

    const pageSizeNumber = Number(pageSize);

    const totalPages = Math.max(
        1,
        Math.ceil(filteredClients.length / pageSizeNumber),
    );

    const pagedClients = useMemo(() => {
        const start = (page - 1) * pageSizeNumber;
        return filteredClients.slice(start, start + pageSizeNumber);
    }, [filteredClients, page, pageSizeNumber]);

    const selectedClient =
        filteredClients.find((client) => client.id === selectedId) ??
        filteredClients[0];

    useEffect(() => {
        if (!selectedClient) {
            setSelectedClientFileContent('');
            setSelectedRouterIp('');
            setRouterDataError('');
            setRouterRules([]);
            return;
        }

        let cancelled = false;

        async function loadSelectedClientFile() {
            try {
                setLoadingRouterData(true);
                setRouterDataError('');

                const response = await vpsApi.readControllerClient(selectedClient.id);

                if (cancelled) return;

                setSelectedClientFileContent(response.content);
            } catch (err) {
                if (cancelled) return;

                setSelectedClientFileContent('');
                setSelectedRouterIp('');
                setRouterDataError(String(err));
            } finally {
                if (!cancelled) {
                    setLoadingRouterData(false);
                }
            }
        }

        loadSelectedClientFile();

        return () => {
            cancelled = true;
        };
    }, [selectedClient?.id]);

    const routerGroups = useMemo(() => {
        if (!selectedClientFileContent) return [];

        const parsed = parseClientTxt(selectedClientFileContent);
        const groups = new Map<string, EditableController[]>();

        parsed.controllers.forEach((controller) => {
            const router = controller.routerVpnIp.trim();

            if (!router) return;

            groups.set(router, [...(groups.get(router) ?? []), controller]);
        });

        return Array.from(groups.entries()).map(([routerVpnIp, controllers]) => ({
            routerVpnIp,
            controllers,
        }));
    }, [selectedClientFileContent]);

    useEffect(() => {
        if (routerGroups.length === 0) {
            setSelectedRouterIp('');
            return;
        }

        setSelectedRouterIp((current) => {
            const currentStillExists = routerGroups.some(
                (group) => group.routerVpnIp === current,
            );

            return currentStillExists ? current : routerGroups[0].routerVpnIp;
        });
    }, [routerGroups]);

    const selectedRouterGroup = routerGroups.find(
        (group) => group.routerVpnIp === selectedRouterIp,
    );

    const routerRulesLoaded = routerRules.length > 0;

    useEffect(() => {
        setRouterRules([]);
        setRouterDataError('');
    }, [selectedRouterIp]);

    async function deleteClient(client: ControllerClient) {
        try {
            setDeletingClientId(client.id);
            setError('');

            await vpsApi.deleteControllerClient(client.id);
            await loadClients();

            setSelectedId((current) => (current === client.id ? '' : current));
            setClientToDelete(null);
        } catch (err) {
            setError(String(err));
        } finally {
            setDeletingClientId('');
        }
    }

    async function loadRouterRules() {
        if (!selectedRouterIp || !routerPassword.trim()) return;

        try {
            setLoadingRouterData(true);
            setRouterDataError('');

            const response = await vpsApi.getRouterFirewallRules(
                selectedRouterIp,
                routerPassword.trim(),
            );

            setRouterRules(response.rules);
        } catch (err) {
            setRouterDataError(String(err));
            setRouterRules([]);
        } finally {
            setLoadingRouterData(false);
        }
    }

    async function refreshRouterPanel() {
        if (!selectedClient) return;

        try {
            setLoadingRouterData(true);
            setRouterDataError('');

            const clientResponse = await vpsApi.readControllerClient(selectedClient.id);
            setSelectedClientFileContent(clientResponse.content);

            if (selectedRouterIp && routerPassword.trim()) {
                const rulesResponse = await vpsApi.getRouterFirewallRules(
                    selectedRouterIp,
                    routerPassword.trim(),
                );

                setRouterRules(rulesResponse.rules);
            }
        } catch (err) {
            setRouterDataError(String(err));
            setRouterRules([]);
        } finally {
            setLoadingRouterData(false);
        }
    }
    return (
        <div className={`flex min-h-full flex-col overflow-y-auto overflow-x-hidden pr-2 ${scrollClass}`}>
            <div className="mb-4 flex shrink-0 items-start justify-between gap-4 rounded-3xl border border-white/10 bg-white/[0.025] px-5 py-4">
                <div className="min-w-0">
                    <h1 className="truncate text-3xl font-black tracking-tight text-white">
                        Controladores
                    </h1>

                    <p className="mt-1 text-sm text-slate-400">
                        Gestão de clientes, controladores, ficheiros VPS e regras do router.
                    </p>
                </div>

                <Button
                    type="button"
                    variant="secondary"
                    onClick={() => loadClients()}
                    disabled={loading}
                    className="shrink-0 rounded-2xl px-4 py-3 font-bold"
                >
                    <RefreshCw size={17} className={loading ? 'animate-spin' : ''} />
                    {loading ? 'A atualizar...' : 'Atualizar'}
                </Button>
            </div>

            {error && (
                <div className="mb-4 shrink-0 rounded-2xl border border-red-500/20 bg-red-500/10 p-4 text-sm text-red-300">
                    Erro ao carregar clientes: {error}
                </div>
            )}

            <div className="grid gap-4 2xl:grid-cols-12">
                <Card className="flex flex-col 2xl:col-span-5">
                    <div className="mb-5 flex shrink-0 items-start justify-between gap-4">
                        <div>
                            <h3 className="font-bold text-white">Clientes</h3>
                            <p className="text-xs text-slate-500">
                                {clients.length} ficheiros em /var/litoral_regas_app/maquinas
                            </p>
                        </div>

                        <Button
                            type="button"
                            onClick={() => setCreatingClient(true)}
                            variant="secondary"
                            className="h-11 rounded-2xl border-blue-500/20 bg-blue-500/10 px-4 font-black text-blue-100 hover:bg-blue-500/15"
                        >
                            <Plus size={17} />
                            Novo Cliente
                        </Button>
                    </div>

                    <div className="mb-4 shrink-0">
                        <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                            Pesquisa
                        </label>

                        <div className="flex items-center gap-2 rounded-xl border border-white/10 bg-slate-950 px-3 py-3">
                            <Search size={17} className="text-slate-500" />

                            <input
                                value={search}
                                onChange={(event) =>
                                    setSearch(event.target.value)
                                }
                                placeholder="Pesquisar cliente..."
                                className="w-full bg-transparent text-sm text-white outline-none placeholder:text-slate-600"
                            />
                        </div>
                    </div>

                    <div className="mb-4 grid shrink-0 grid-cols-2 gap-3">
                        <div>
                            <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                                Estado
                            </label>

                            <Select
                                value={controllerFilter}
                                options={controllerFilterOptions}
                                onChange={setControllerFilter}
                            />
                        </div>

                        <div>
                            <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                                Paginação
                            </label>

                            <Select
                                value={pageSize}
                                options={pageSizeOptions}
                                onChange={setPageSize}
                            />
                        </div>
                    </div>

                    <div
                        className={`h-[420px] space-y-2 overflow-y-auto pr-2 lg:h-[500px] 2xl:h-[620px] ${scrollClass}`}
                    >
                        {loading && clients.length === 0 && (
                            <div className="rounded-2xl border border-white/10 bg-white/[0.025] p-4 text-sm text-slate-400">
                                A carregar clientes da VPS...
                            </div>
                        )}

                        {!loading && filteredClients.length === 0 && (
                            <div className="rounded-2xl border border-white/10 bg-white/[0.025] p-4 text-sm text-slate-400">
                                Nenhum cliente encontrado.
                            </div>
                        )}

                        {pagedClients.map((client) => {
                            const active = selectedClient?.id === client.id;

                            return (
                                <button
                                    key={client.id}
                                    type="button"
                                    onClick={() => setSelectedId(client.id)}
                                    className={`group w-full rounded-2xl border p-4 text-left transition-all duration-200 ${active
                                        ? 'border-blue-500/25 bg-blue-500/[0.08]'
                                        : 'border-white/10 bg-white/[0.025] hover:border-white/15 hover:bg-white/[0.045]'
                                        }`}
                                >
                                    <div className="flex items-start justify-between gap-4">
                                        <div className="min-w-0 flex-1">
                                            <div className="flex items-center gap-2">
                                                <p className="truncate text-[15px] font-bold tracking-tight text-white">
                                                    {client.name}
                                                </p>

                                                <div
                                                    className={`h-2 w-2 rounded-full ${client.controller_count > 0
                                                        ? 'bg-blue-400'
                                                        : 'bg-slate-600'
                                                        }`}
                                                />
                                            </div>

                                            <p className="mt-1 truncate font-mono text-[11px] text-slate-500">
                                                {client.file}
                                            </p>

                                            <div className="mt-3 flex items-center gap-2 text-xs text-slate-400">
                                                <ShieldCheck
                                                    size={13}
                                                    className={
                                                        client.controller_count > 0
                                                            ? 'text-blue-300'
                                                            : 'text-slate-600'
                                                    }
                                                />

                                                <span>
                                                    {client.controller_count > 0
                                                        ? 'Com controladores'
                                                        : 'Sem controladores'}
                                                </span>
                                            </div>
                                        </div>

                                        <div className="flex shrink-0 items-center gap-2">
                                            <div
                                                className={`flex h-8 min-w-8 items-center justify-center rounded-full px-2 text-xs font-black ${client.controller_count > 0
                                                    ? 'bg-blue-500/10 text-blue-100'
                                                    : 'bg-white/[0.04] text-slate-400'
                                                    }`}
                                            >
                                                {client.controller_count}
                                            </div>

                                            <button
                                                type="button"
                                                onClick={(event) => {
                                                    event.stopPropagation();
                                                    setClientToDelete(client);
                                                }}
                                                className="flex h-8 w-8 items-center justify-center rounded-xl text-slate-500 transition hover:bg-red-500/10 hover:text-red-300"
                                            >
                                                <Trash2 size={16} />
                                            </button>
                                        </div>
                                    </div>
                                </button>
                            );
                        })}
                    </div>

                    <div className="mt-4 flex shrink-0 items-center justify-between gap-3 border-t border-white/10 pt-4 text-xs text-slate-400">
                        <span>
                            Página {page} de {totalPages} ·{' '}
                            {filteredClients.length} resultados
                        </span>

                        <div className="flex gap-2">
                            <Button
                                type="button"
                                variant="secondary"
                                disabled={page <= 1}
                                onClick={() => setPage((current) => Math.max(1, current - 1))}
                                className="rounded-xl px-3 py-2 text-xs font-bold"
                            >
                                Anterior
                            </Button>

                            <Button
                                type="button"
                                variant="secondary"
                                disabled={page >= totalPages}
                                onClick={() => setPage((current) => Math.min(totalPages, current + 1))}
                                className="rounded-xl px-3 py-2 text-xs font-bold"
                            >
                                Seguinte
                            </Button>
                        </div>
                    </div>
                </Card>

                <div className="flex min-h-0 flex-col gap-4 xl:col-span-7">
                    <Card className="shrink-0 overflow-hidden">
                        {selectedClient ? (
                            <div className="flex items-center justify-between gap-4">
                                <div className="min-w-0">
                                    <p className="text-xs font-bold uppercase tracking-[0.18em] text-blue-300/70">
                                        Cliente selecionado
                                    </p>

                                    <div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1">
                                        <h3 className="truncate text-2xl font-black text-white">
                                            {selectedClient.name}
                                        </h3>

                                        <span className="rounded-xl bg-blue-500/10 px-3 py-1 text-xs font-black text-blue-100">
                                            {selectedClient.controller_count} controlador(es)
                                        </span>

                                        <span className="truncate font-mono text-xs text-slate-500">
                                            {selectedClient.file}
                                        </span>
                                    </div>

                                    <p className="mt-2 truncate font-mono text-xs text-slate-600">
                                        {selectedClient.path}
                                    </p>
                                </div>

                                <Button
                                    type="button"
                                    onClick={() => setEditingClient(selectedClient)}
                                    className="shrink-0 rounded-2xl px-4 py-3 font-black"
                                >
                                    <Pencil size={17} />
                                    Editar Cliente
                                </Button>
                            </div>
                        ) : (
                            <div className="flex min-h-[90px] items-center justify-center text-slate-500">
                                Nenhum cliente selecionado.
                            </div>
                        )}
                    </Card>

                    <Card className="flex flex-col">
                        {selectedClient ? (
                            <>
                                <div className="mb-4 flex shrink-0 items-center justify-between gap-4">
                                    <div className="min-w-0">
                                        <p className="text-xs font-bold uppercase tracking-[0.18em] text-blue-300/70">
                                            Router / Firewall
                                        </p>

                                        <h3 className="mt-2 text-xl font-black text-white">
                                            Gestão de DNAT
                                        </h3>

                                        <p className="mt-1 text-sm text-slate-500">
                                            Seleciona o router, confirma a password e carrega as regras UCI.
                                        </p>
                                    </div>

                                    <Button
                                        type="button"
                                        variant="secondary"
                                        onClick={refreshRouterPanel}
                                        disabled={!selectedRouterIp || !routerPassword.trim() || loadingRouterData}
                                        className="shrink-0 rounded-2xl px-4 py-3 font-bold"
                                    >
                                        <RefreshCw size={16} className={loadingRouterData ? 'animate-spin' : ''} />
                                        {loadingRouterData ? 'A carregar...' : 'Carregar regras'}
                                    </Button>
                                </div>

                                <div className="mb-4 shrink-0 rounded-2xl border border-white/10 bg-white/[0.025] p-4">
                                    <div className="grid gap-3 xl:grid-cols-2">
                                        <div>
                                            <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                                                Router alvo
                                            </label>

                                            <Select
                                                value={selectedRouterIp}
                                                onChange={setSelectedRouterIp}
                                                options={[
                                                    {
                                                        value: '',
                                                        label: routerGroups.length === 0
                                                            ? 'Nenhum router detetado'
                                                            : 'Selecionar router...',
                                                    },
                                                    ...routerGroups.map((group) => ({
                                                        value: group.routerVpnIp,
                                                        label: `${group.routerVpnIp} · ${group.controllers.length} controlador(es)`,
                                                    })),
                                                ]}
                                            />
                                        </div>

                                        <div>
                                            <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                                                Palavra-passe SSH
                                            </label>

                                            <div className="relative">
                                                <input
                                                    type={showRouterPassword ? 'text' : 'password'}
                                                    value={routerPassword}
                                                    onChange={(event) => setRouterPassword(event.target.value)}
                                                    placeholder="Password do router"
                                                    className="w-full rounded-xl border border-white/10 bg-slate-950 px-3 py-3 pr-12 font-mono text-sm text-white outline-none transition placeholder:text-slate-600 focus:border-blue-500/40"
                                                />

                                                <button
                                                    type="button"
                                                    onClick={() => setShowRouterPassword((current) => !current)}
                                                    className="absolute right-3 top-1/2 flex -translate-y-1/2 items-center justify-center rounded-lg p-1.5 text-slate-500 transition hover:bg-white/[0.06] hover:text-white"
                                                >
                                                    {showRouterPassword ? <EyeOff size={16} /> : <Eye size={16} />}
                                                </button>
                                            </div>
                                        </div>
                                    </div>

                                    <div className="mt-3 flex items-center justify-between gap-3 rounded-xl border border-white/10 bg-slate-950 px-4 py-3">
                                        <div className="flex min-w-0 items-center gap-3">
                                            <div className="shrink-0 rounded-xl bg-blue-500/10 p-2 text-blue-300">
                                                <Router size={17} />
                                            </div>

                                            <div className="min-w-0">
                                                <p className="text-xs text-slate-500">Estado</p>

                                                <p className="truncate font-bold text-white">
                                                    {routerDataError
                                                        ? 'Falha ao comunicar com o router'
                                                        : loadingRouterData
                                                            ? 'A analisar router...'
                                                            : routerRules.length > 0
                                                                ? `${routerRules.length} regra(s) carregada(s)`
                                                                : selectedRouterIp
                                                                    ? 'Pronto para carregar regras'
                                                                    : 'Nenhum router selecionado'}
                                                </p>
                                            </div>
                                        </div>

                                        <div
                                            className={`shrink-0 rounded-xl px-3 py-1.5 text-xs font-bold transition ${routerDataError
                                                ? 'border border-red-500/20 bg-red-500/10 text-red-300'
                                                : loadingRouterData
                                                    ? 'border border-blue-500/20 bg-blue-500/10 text-blue-200'
                                                    : routerRules.length > 0
                                                        ? 'border border-emerald-500/20 bg-emerald-500/10 text-emerald-200'
                                                        : 'border border-white/10 bg-white/[0.03] text-slate-400'
                                                }`}
                                        >
                                            {routerDataError
                                                ? 'Router inacessível'
                                                : loadingRouterData
                                                    ? 'A ligar...'
                                                    : routerRules.length > 0
                                                        ? 'Ligado'
                                                        : 'Não carregado'}
                                        </div>
                                    </div>

                                    {routerDataError && (
                                        <div className="mt-3 rounded-xl border border-red-500/20 bg-red-500/10 px-4 py-3">
                                            <p className="text-sm font-bold text-red-200">
                                                Não foi possível carregar as regras do router.
                                            </p>

                                            <p className="mt-1 break-all font-mono text-xs text-red-300/80">
                                                {routerDataError}
                                            </p>
                                        </div>
                                    )}
                                </div>

                                <div className="grid min-h-0 flex-1 gap-4 overflow-hidden xl:grid-cols-12">
                                    <div className="flex min-h-0 flex-col rounded-2xl border border-white/10 bg-white/[0.025] p-4 xl:col-span-4">
                                        <div className="shrink-0">
                                            <h4 className="font-bold text-white">Controladores</h4>
                                            <p className="mt-1 text-xs text-slate-500">
                                                Associados ao router selecionado.
                                            </p>
                                        </div>

                                        <div className={`mt-4 min-h-0 flex-1 space-y-2 overflow-y-auto pr-2 ${scrollClass}`}>
                                            {!selectedRouterGroup ? (
                                                <div className="flex min-h-[140px] items-center justify-center rounded-2xl border border-dashed border-white/10 bg-black/10 text-sm text-slate-500">
                                                    Seleciona um router.
                                                </div>
                                            ) : (
                                                selectedRouterGroup.controllers.map((controller) => (
                                                    <div
                                                        key={controller.id}
                                                        className="rounded-2xl border border-white/10 bg-black/15 p-3"
                                                    >
                                                        <div className="flex items-center justify-between gap-3">
                                                            <div className="min-w-0">
                                                                <p className="truncate font-bold text-white">
                                                                    {controller.name}
                                                                </p>

                                                                <p className="mt-1 font-mono text-xs text-slate-500">
                                                                    Externa {controller.externalPort}
                                                                </p>
                                                            </div>

                                                            <span className="rounded-xl bg-blue-500/10 px-2.5 py-1 font-mono text-xs font-black text-blue-100">
                                                                {controller.password}
                                                            </span>
                                                        </div>
                                                    </div>
                                                ))
                                            )}
                                        </div>
                                    </div>

                                    <div className="flex min-h-0 flex-col rounded-2xl border border-white/10 bg-white/[0.025] p-4 xl:col-span-8">
                                        <div className="mb-4 flex shrink-0 items-center justify-between gap-3">
                                            <div>
                                                <h4 className="font-bold text-white">Regras DNAT</h4>
                                                <p className="text-xs text-slate-500">
                                                    Portas externas e destinos internos.
                                                </p>
                                            </div>

                                            <Button
                                                type="button"
                                                disabled={
                                                    !selectedRouterGroup ||
                                                    !selectedRouterIp ||
                                                    !routerPassword.trim() ||
                                                    !routerRulesLoaded
                                                }
                                                onClick={() => setCreatingRule(true)}
                                                className="rounded-2xl px-4 py-3 font-black"
                                            >
                                                <Plus size={16} />
                                                Nova Regra
                                            </Button>
                                        </div>

                                        <div className={`min-h-0 flex-1 space-y-2 overflow-y-auto pr-2 ${scrollClass}`}>
                                            {routerRules.length === 0 ? (
                                                <div className="flex min-h-[180px] flex-col items-center justify-center rounded-2xl border border-dashed border-white/10 bg-black/10 text-center text-sm text-slate-500">
                                                    <p>Nenhuma regra carregada ainda.</p>
                                                    <p className="mt-1 text-xs text-slate-600">
                                                        Carrega o router para listar as regras DNAT.
                                                    </p>
                                                </div>
                                            ) : (
                                                routerRules.map((rule) => (
                                                    <div
                                                        key={rule.id}
                                                        className="rounded-2xl border border-white/10 bg-black/15 p-3 transition hover:border-white/15 hover:bg-white/[0.035]"
                                                    >
                                                        <div className="flex items-center justify-between gap-3">
                                                            <div className="min-w-0">
                                                                <p className="truncate font-bold text-white">
                                                                    {rule.name || rule.id}
                                                                </p>

                                                                <p className="mt-1 font-mono text-xs text-slate-500">
                                                                    {rule.externalPort} → {rule.internalIp}:{rule.internalPort}
                                                                </p>
                                                            </div>

                                                            <div className="flex shrink-0 items-center gap-2">
                                                                <span className="rounded-xl bg-blue-500/10 px-2.5 py-1 text-xs font-black uppercase text-blue-100">
                                                                    {rule.proto}
                                                                </span>

                                                                <button
                                                                    type="button"
                                                                    onClick={() => setRuleToUpdate(rule)}
                                                                    className="rounded-xl border border-white/10 bg-white/[0.03] px-3 py-2 text-xs font-black text-slate-300 transition hover:bg-white/[0.06] hover:text-white"
                                                                >
                                                                    Editar
                                                                </button>

                                                                <button
                                                                    type="button"
                                                                    onClick={() => setRuleToDelete(rule)}
                                                                    className="flex h-9 w-9 items-center justify-center rounded-xl text-slate-500 transition hover:bg-red-500/10 hover:text-red-300"
                                                                >
                                                                    <Trash2 size={16} />
                                                                </button>
                                                            </div>
                                                        </div>
                                                    </div>
                                                ))
                                            )}
                                        </div>
                                    </div>
                                </div>
                            </>
                        ) : (
                            <div className="flex h-full items-center justify-center text-slate-500">
                                Seleciona um cliente para gerir o router.
                            </div>
                        )}
                    </Card>
                </div>
            </div>

            {editingClient && (
                <EditClientModal
                    client={editingClient}
                    onClose={() => setEditingClient(null)}
                    onSaved={async () => {
                        await loadClients();
                        await refreshRouterPanel();
                    }}
                />
            )}

            {creatingClient && (
                <CreateClientModal
                    clients={clients}
                    onClose={() => setCreatingClient(false)}
                    onSaved={(clientId) => {
                        setSearch(clientId);
                        setControllerFilter('all');
                        setPage(1);

                        return loadClients(clientId)
                    }}
                />
            )}

            {clientToDelete && (
                <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm">
                    <div className="w-full max-w-md overflow-hidden rounded-3xl border border-white/10 bg-[#050b1a] shadow-2xl">
                        <div className="border-b border-white/10 px-6 py-5">
                            <p className="text-xs font-bold uppercase tracking-[0.18em] text-red-300/80">
                                Apagar Cliente
                            </p>

                            <h2 className="mt-2 text-xl font-black text-white">
                                Tem a certeza?
                            </h2>

                            <p className="mt-2 text-sm text-slate-400">
                                Vai apagar o ficheiro do cliente{' '}
                                <span className="font-bold text-white">
                                    {clientToDelete.name}
                                </span>
                                .
                            </p>
                        </div>

                        <div className="flex justify-end gap-3 px-6 py-4">
                            <Button
                                type="button"
                                variant="secondary"
                                onClick={() => setClientToDelete(null)}
                                className="rounded-2xl px-4 py-3 font-bold"
                            >
                                Cancelar
                            </Button>

                            <Button
                                type="button"
                                variant="danger"
                                disabled={deletingClientId === clientToDelete.id}
                                onClick={() => deleteClient(clientToDelete)}
                                className="rounded-2xl px-5 py-3 font-black"
                            >
                                <Trash2 size={16} />
                                {deletingClientId === clientToDelete.id
                                    ? 'A apagar...'
                                    : 'Apagar'}
                            </Button>
                        </div>
                    </div>
                </div>
            )}

            {creatingRule && (
                <RuleModal
                    title="Nova Regra"
                    routerHost={selectedRouterIp}
                    routerPassword={routerPassword}
                    defaultExternalPort={selectedRouterGroup?.controllers[0]?.externalPort ?? ''}
                    onClose={() => setCreatingRule(false)}
                    onSaved={async () => {
                        setCreatingRule(false);
                        await loadRouterRules();
                    }}
                />
            )}

            {ruleToUpdate && (
                <RuleModal
                    title="Editar Regra"
                    routerHost={selectedRouterIp}
                    routerPassword={routerPassword}
                    rule={ruleToUpdate}
                    onClose={() => setRuleToUpdate(null)}
                    onSaved={async () => {
                        setRuleToUpdate(null);
                        await loadRouterRules();
                    }}
                />
            )}

            {ruleToDelete && (
                <DeleteRuleModal
                    rule={ruleToDelete}
                    loading={savingRule}
                    onClose={() => setRuleToDelete(null)}
                    onConfirm={async () => {
                        try {
                            setSavingRule(true);
                            await vpsApi.deleteRouterFirewallRule(
                                selectedRouterIp,
                                routerPassword,
                                ruleToDelete.id,
                            );
                            setRuleToDelete(null);
                            await loadRouterRules();
                        } finally {
                            setSavingRule(false);
                        }
                    }}
                />
            )}
        </div>
    );
}

function MiniStat({
    icon,
    label,
    value,
}: {
    icon: React.ReactNode;
    label: string;
    value: string;
}) {
    return (
        <div className="rounded-2xl border border-white/10 bg-white/[0.03] p-4">
            <div className="mb-3 w-fit rounded-xl bg-blue-500/10 p-2 text-blue-300">
                {icon}
            </div>

            <p className="text-xs text-slate-500">
                {label}
            </p>

            <p className="mt-1 truncate font-black text-white">
                {value}
            </p>
        </div>
    );
}

type EditableController = {
    id: string;
    name: string;
    routerVpnIp: string;
    externalPort: string;
    password: string;
};

function CreateClientModal({
    clients,
    onClose,
    onSaved,
}: {
    clients: ControllerClient[];
    onClose: () => void;
    onSaved: (clientId: string) => Promise<void> | void;
}) {
    const [name, setName] = useState('');
    const [password, setPassword] = useState(generateClientPassword());
    const [saving, setSaving] = useState(false);
    const [error, setError] = useState('');

    const cleanName = name.trim();

    const nameError = useMemo(() => {
        if (!cleanName) return 'O nome do cliente é obrigatório.';

        if (!/^[A-Za-z0-9 _-]+$/.test(cleanName)) {
            return 'Usa apenas letras, números, espaços, "_" ou "-".';
        }

        if (hasTxtSeparator(cleanName)) {
            return 'O nome não pode conter "xxx" ou quebras de linha.';
        }

        const clientId = normalizeClientId(cleanName);

        const duplicateClient = clients.some(
            (client) => normalizeClientId(client.id) === clientId,
        );

        if (duplicateClient) {
            return 'Já existe um cliente com esse nome.';
        }

        return '';
    }, [cleanName, clients]);

    const passwordError = useMemo(() => {
        if (!password.trim()) return 'A palavra-passe é obrigatória.';

        if (hasTxtSeparator(password)) {
            return 'A palavra-passe não pode conter "xxx" ou quebras de linha.';
        }

        return '';
    }, [password]);

    const formIsValid = !nameError && !passwordError;

    async function createClient() {
        try {
            setSaving(true);
            setError('');

            await vpsApi.createControllerClient(cleanName, password);
            await onSaved(cleanName);

            onClose();
        } catch (err) {
            setError(String(err));
        } finally {
            setSaving(false);
        }
    }

    function normalizeClientId(value: string) {
        return value.trim().toLowerCase().replace(/\s+/g, '_');
    }

    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm">
            <div className="w-full max-w-xl overflow-hidden rounded-3xl border border-white/10 bg-[#050b1a] shadow-2xl">
                <div className="flex items-start justify-between gap-4 border-b border-white/10 px-6 py-5">
                    <div>
                        <p className="text-xs font-bold uppercase tracking-[0.18em] text-blue-300/70">
                            Novo Cliente
                        </p>

                        <h2 className="mt-2 text-2xl font-black text-white">
                            Criar ficheiro VPS
                        </h2>

                        <p className="mt-1 text-sm text-slate-500">
                            Cria um novo cliente com apenas a primeira linha do ficheiro.
                        </p>
                    </div>

                    <button
                        type="button"
                        onClick={onClose}
                        className="rounded-2xl border border-white/10 bg-white/[0.03] p-3 text-slate-400 transition hover:bg-white/[0.06] hover:text-white"
                    >
                        <X size={18} />
                    </button>
                </div>

                <div className="space-y-5 px-6 py-6">
                    {error && (
                        <div className="rounded-xl border border-red-500/20 bg-red-500/10 px-3 py-2 text-xs font-medium text-red-300">
                            Erro ao criar cliente: {error}
                        </div>
                    )}

                    <Field
                        label="Nome do cliente"
                        value={name}
                        error={nameError}
                        placeholder="Ex: Adoraflor"
                        onChange={(value) =>
                            setName(value.replace(/[^\w\- ]/g, ''))
                        }
                        autoFocus
                    />

                    <div>
                        <Field
                            label="Palavra-passe"
                            value={password}
                            error={passwordError}
                            onChange={setPassword}
                        />

                        <Button
                            type="button"
                            variant="secondary"
                            onClick={() => setPassword(generateClientPassword())}
                            className="mt-3 rounded-xl text-xs font-bold"
                        >
                            Gerar password
                        </Button>
                    </div>
                </div>

                <div className="flex items-center justify-end gap-3 border-t border-white/10 px-6 py-4">
                    <Button
                        type="button"
                        variant="secondary"
                        onClick={onClose}
                        className="rounded-2xl px-4 py-3 font-bold"
                    >
                        Cancelar
                    </Button>

                    <Button
                        type="button"
                        onClick={createClient}
                        disabled={!formIsValid || saving}
                        className="rounded-2xl px-5 py-3 font-black disabled:bg-slate-700 disabled:text-slate-400"
                    >
                        <Plus size={16} />
                        {saving ? 'A criar...' : 'Criar Cliente'}
                    </Button>
                </div>
            </div>
        </div>
    );
}

function EditClientModal({
    client,
    onClose,
    onSaved,
}: {
    client: ControllerClient;
    onClose: () => void;
    onSaved: () => Promise<void> | void;
}) {
    const [clientPassword, setClientPassword] = useState('');
    const [controllers, setControllers] = useState<EditableController[]>([]);
    const [loadingClientFile, setLoadingClientFile] = useState(true);
    const [clientFileError, setClientFileError] = useState('');
    const [saving, setSaving] = useState(false);
    const [saveError, setSaveError] = useState('');

    useEffect(() => {
        let cancelled = false;

        async function loadClientFile() {
            try {
                setLoadingClientFile(true);
                setClientFileError('');

                const response = await vpsApi.readControllerClient(client.id);

                if (cancelled) return;

                const parsed = parseClientTxt(response.content);

                setClientPassword(parsed.password);
                setControllers(parsed.controllers);
            } catch (err) {
                if (cancelled) return;

                setClientFileError(String(err));
                setClientPassword('');
                setControllers([]);
            } finally {
                if (!cancelled) {
                    setLoadingClientFile(false);
                }
            }
        }

        loadClientFile();

        return () => {
            cancelled = true;
        };
    }, [client.id]);

    const controllerErrors = useMemo(
        () => controllers.map(validateController),
        [controllers],
    );

    const clientPasswordError = useMemo(() => {
        if (loadingClientFile) return '';
        if (!clientPassword.trim()) return 'A palavra-passe é obrigatória.';

        if (hasTxtSeparator(clientPassword)) {
            return 'A palavra-passe não pode conter "xxx" ou quebras de linha.';
        }

        return '';
    }, [clientPassword, loadingClientFile]);

    const formIsValid =
        !loadingClientFile &&
        !clientFileError &&
        !clientPasswordError &&
        controllers.length > 0 &&
        controllerErrors.every((errors) => !hasErrors(errors));

    const txtPreview = useMemo(() => {
        const lines = [
            clientPassword,
            ...controllers.map(
                (controller) =>
                    `${controller.name}xxx${controller.routerVpnIp}xxx${controller.externalPort}xxx${controller.password}`,
            ),
        ];

        return `${lines.join('\n')}\n`;
    }, [clientPassword, controllers]);

    function updateController(
        id: string,
        patch: Partial<EditableController>,
    ) {
        setControllers((current) =>
            current.map((controller) =>
                controller.id === id
                    ? { ...controller, ...patch }
                    : controller,
            ),
        );
    }

    function addController() {
        setControllers((current) => [
            ...current,
            {
                id: crypto.randomUUID(),
                name: `Controlador${current.length + 1}`,
                routerVpnIp: '',
                externalPort: getNextAvailablePort(current),
                password: '111111',
            },
        ]);
    }

    function removeController(id: string) {
        setControllers((current) =>
            current.filter((controller) => controller.id !== id),
        );
    }

    async function saveClientFile() {
        try {
            setSaving(true);
            setSaveError('');

            await vpsApi.updateControllerClient(client.id, txtPreview);
            await onSaved();

            onClose();
        } catch (err) {
            setSaveError(String(err));
        } finally {
            setSaving(false);
        }
    }

    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm">
            <div className="flex max-h-[90vh] w-full max-w-6xl flex-col overflow-hidden rounded-3xl border border-white/10 bg-[#050b1a] shadow-2xl">
                <div className="flex items-start justify-between gap-4 border-b border-white/10 px-6 py-5">
                    <div>
                        <p className="text-xs font-bold uppercase tracking-[0.18em] text-blue-300/70">
                            Editar Cliente
                        </p>

                        <h2 className="mt-2 text-2xl font-black text-white">
                            {client.name}
                        </h2>

                        <p className="mt-1 break-all font-mono text-xs text-slate-500">
                            {client.path}
                        </p>
                    </div>

                    <button
                        type="button"
                        onClick={onClose}
                        className="rounded-2xl border border-white/10 bg-white/[0.03] p-3 text-slate-400 transition hover:bg-white/[0.06] hover:text-white"
                    >
                        <X size={18} />
                    </button>
                </div>

                {saveError && (
                    <div className="mx-6 mt-3 rounded-xl border border-red-500/20 bg-red-500/10 px-3 py-2 text-xs font-medium text-red-300">
                        Erro ao guardar: {saveError}
                    </div>
                )}

                {loadingClientFile ? (
                    <div className="flex min-h-[420px] items-center justify-center text-sm text-slate-400">
                        A carregar ficheiro do cliente...
                    </div>
                ) : clientFileError ? (
                    <div className="m-6 rounded-2xl border border-red-500/20 bg-red-500/10 p-4 text-sm text-red-300">
                        Erro ao carregar ficheiro do cliente: {clientFileError}
                    </div>
                ) : (
                    <div className={`grid min-h-0 flex-1 gap-4 overflow-hidden px-6 pb-6 xl:grid-cols-12 ${saveError ? 'pt-4' : 'pt-6'}`}>
                        <div className="space-y-4 xl:col-span-7">
                            <div className="rounded-3xl border border-white/10 bg-white/[0.025] p-5">
                                <h3 className="font-bold text-white">
                                    Dados do Cliente
                                </h3>

                                <div className="mt-4">
                                    <Field
                                        label="Palavra-passe / primeira linha do ficheiro"
                                        value={clientPassword}
                                        error={clientPasswordError}
                                        onChange={setClientPassword}
                                    />
                                </div>
                            </div>

                            <div className="rounded-3xl border border-white/10 bg-white/[0.025] p-5">
                                <div className="mb-4 flex items-center justify-between gap-3">
                                    <div>
                                        <h3 className="font-bold text-white">
                                            Controladores
                                        </h3>

                                        <p className="text-xs text-slate-500">
                                            Entradas carregadas do ficheiro real do cliente.
                                        </p>
                                    </div>

                                    <Button
                                        type="button"
                                        variant="secondary"
                                        onClick={addController}
                                        className="rounded-2xl border-blue-500/20 bg-blue-500/15 px-4 py-3 font-bold text-blue-100 hover:bg-blue-500/20"
                                    >
                                        <Plus size={16} />
                                        Adicionar
                                    </Button>
                                </div>

                                <div className={`max-h-[360px] space-y-3 overflow-y-auto pr-2 ${scrollClass}`}>
                                    {controllers.map((controller, index) => (
                                        <div
                                            key={controller.id}
                                            className="rounded-2xl border border-white/10 bg-black/15 p-4"
                                        >
                                            <div className="mb-3 flex items-center justify-between">
                                                <p className="text-sm font-bold text-white">
                                                    Controlador #{index + 1}
                                                </p>

                                                <button
                                                    type="button"
                                                    onClick={() =>
                                                        removeController(controller.id)
                                                    }
                                                    className="rounded-xl p-2 text-slate-500 transition hover:bg-red-500/10 hover:text-red-300"
                                                >
                                                    <Trash2 size={16} />
                                                </button>
                                            </div>

                                            <div className="grid gap-3 md:grid-cols-2">
                                                <Field
                                                    label="Nome"
                                                    value={controller.name}
                                                    error={controllerErrors[index]?.name}
                                                    onChange={(value) =>
                                                        updateController(controller.id, {
                                                            name: value.replace(
                                                                /[^\w\- ]/g,
                                                                '',
                                                            ),
                                                        })
                                                    }
                                                />

                                                <SmartRouterField
                                                    label="Router / Host"
                                                    value={controller.routerVpnIp}
                                                    error={controllerErrors[index]?.routerVpnIp}
                                                    onChange={(value) =>
                                                        updateController(controller.id, {
                                                            routerVpnIp: value,
                                                        })
                                                    }
                                                />

                                                <Field
                                                    label="Porta externa"
                                                    type="number"
                                                    min={5900}
                                                    max={5999}
                                                    step={1}
                                                    value={controller.externalPort}
                                                    error={
                                                        controllerErrors[index]?.externalPort
                                                    }
                                                    inputMode="numeric"
                                                    onChange={(value) =>
                                                        updateController(controller.id, {
                                                            externalPort: value,
                                                        })
                                                    }
                                                />

                                                <Field
                                                    label="PIN / Password"
                                                    value={controller.password}
                                                    error={controllerErrors[index]?.password}
                                                    inputMode="numeric"
                                                    maxLength={6}
                                                    onChange={(value) =>
                                                        updateController(controller.id, {
                                                            password: value
                                                                .replace(/\D/g, '')
                                                                .slice(0, 6),
                                                        })
                                                    }
                                                />
                                            </div>
                                        </div>
                                    ))}
                                </div>
                            </div>
                        </div>

                        <div className="xl:col-span-5">
                            <div className="sticky top-0 rounded-3xl border border-white/10 bg-white/[0.025] p-5">
                                <div className="mb-4 flex items-center justify-between gap-3">
                                    <div>
                                        <h3 className="font-bold text-white">
                                            Preview .txt
                                        </h3>

                                        <p className="text-xs text-slate-500">
                                            Conteúdo exato a guardar no ficheiro.
                                        </p>
                                    </div>

                                    <span className="rounded-2xl bg-white/[0.04] px-3 py-2 text-xs font-bold text-slate-300">
                                        {controllers.length} entradas
                                    </span>
                                </div>

                                <pre className={`max-h-[520px] overflow-auto whitespace-pre-wrap rounded-2xl border border-white/10 bg-black/30 p-4 font-mono text-xs leading-6 text-slate-300 ${scrollClass}`}>
                                    {txtPreview}
                                </pre>
                            </div>
                        </div>
                    </div>
                )}

                <div className="flex items-center justify-between gap-3 border-t border-white/10 px-6 py-4">
                    <p className="text-xs text-slate-500">
                        Dados carregados diretamente do ficheiro do cliente.
                    </p>

                    <div className="flex gap-3">
                        <Button
                            type="button"
                            variant="secondary"
                            onClick={onClose}
                            className="rounded-2xl px-4 py-3 font-bold"
                        >
                            Cancelar
                        </Button>

                        <Button
                            type="button"
                            onClick={saveClientFile}
                            disabled={!formIsValid || saving}
                            className="rounded-2xl px-5 py-3 font-black disabled:bg-slate-700 disabled:text-slate-400"
                        >
                            <Save size={16} />
                            {saving ? 'A guardar...' : 'Guardar alterações'}
                        </Button>
                    </div>
                </div>
            </div>
        </div>
    );
}

function RuleModal({
    title,
    routerHost,
    routerPassword,
    rule,
    defaultExternalPort = '',
    onClose,
    onSaved,
}: {
    title: string;
    routerHost: string;
    routerPassword: string;
    rule?: RouterFirewallRule;
    defaultExternalPort?: string;
    onClose: () => void;
    onSaved: () => Promise<void> | void;
}) {
    const editing = Boolean(rule);
    const [id, setId] = useState(rule?.id ?? '');
    const [name, setName] = useState(rule?.name ?? '');
    const [proto, setProto] = useState(rule?.proto ?? 'tcp');
    const [srcZone, setSrcZone] = useState(rule?.srcZone ?? 'vpn');
    const [destZone, setDestZone] = useState(rule?.destZone ?? 'lan');
    const [externalPort, setExternalPort] = useState(rule?.externalPort ?? defaultExternalPort);
    const [internalIp, setInternalIp] = useState(rule?.internalIp ?? '');
    const [internalPort, setInternalPort] = useState(rule?.internalPort ?? '5900');
    const [saving, setSaving] = useState(false);
    const [error, setError] = useState('');

    const cleanId = id.trim();
    const formIsValid =
        cleanId &&
        name.trim() &&
        proto.trim() &&
        srcZone.trim() &&
        destZone.trim() &&
        isValidPort(externalPort) &&
        isValidPort(internalPort) &&
        IPV4_REGEX.test(internalIp.trim());

    async function saveRule() {
        if (!formIsValid) return;

        const payload: RouterFirewallRule = {
            id: cleanId,
            name: name.trim(),
            proto: proto.trim(),
            srcZone: srcZone.trim(),
            destZone: destZone.trim(),
            externalPort: externalPort.trim(),
            internalIp: internalIp.trim(),
            internalPort: internalPort.trim(),
            target: 'DNAT',
        };

        try {
            setSaving(true);
            setError('');

            if (editing) {
                await vpsApi.updateRouterFirewallRule(routerHost, routerPassword, payload);
            } else {
                await vpsApi.createRouterFirewallRule(routerHost, routerPassword, payload);
            }

            await onSaved();
        } catch (err) {
            setError(String(err));
        } finally {
            setSaving(false);
        }
    }

    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm">
            <div className="w-full max-w-3xl overflow-hidden rounded-3xl border border-white/10 bg-[#050b1a] shadow-2xl">
                <div className="flex items-start justify-between gap-4 border-b border-white/10 px-6 py-5">
                    <div>
                        <p className="text-xs font-bold uppercase tracking-[0.18em] text-blue-300/70">
                            Regra DNAT
                        </p>

                        <h2 className="mt-2 text-2xl font-black text-white">
                            {title}
                        </h2>

                        <p className="mt-1 font-mono text-xs text-slate-500">
                            Router: {routerHost}
                        </p>
                    </div>

                    <button
                        type="button"
                        onClick={onClose}
                        className="rounded-2xl border border-white/10 bg-white/[0.03] p-3 text-slate-400 transition hover:bg-white/[0.06] hover:text-white"
                    >
                        <X size={18} />
                    </button>
                </div>

                <div className="space-y-4 px-6 py-6">
                    {error && (
                        <div className="rounded-xl border border-red-500/20 bg-red-500/10 px-3 py-2 text-xs font-medium text-red-300">
                            Erro ao guardar regra: {error}
                        </div>
                    )}

                    <div className="grid gap-4 md:grid-cols-2">
                        <Field
                            label="ID técnico"
                            value={id}
                            disabled={editing}
                            placeholder="dnat_hmi_1_vnc"
                            onChange={(value) =>
                                setId(value.replace(/[^\w-]/g, '').toLowerCase())
                            }
                        />

                        <Field
                            label="Nome"
                            value={name}
                            placeholder="HMI 1 - VNC"
                            onChange={setName}
                        />

                        <Field
                            label="Protocolo"
                            value={proto}
                            placeholder="tcp"
                            onChange={(value) => setProto(value.toLowerCase())}
                        />

                        <Field
                            label="Porta externa"
                            type="number"
                            min={1}
                            max={65535}
                            step={1}
                            value={externalPort}
                            error={externalPort && !isValidPort(externalPort) ? 'Porta inválida.' : ''}
                            inputMode="numeric"
                            placeholder="5900"
                            onChange={(value) => setExternalPort(value.replace(/\D/g, ''))}
                        />

                        <IpField
                            label="IP interno"
                            value={internalIp}
                            error={internalIp && !IPV4_REGEX.test(internalIp) ? 'IPv4 inválido.' : ''}
                            onChange={setInternalIp}
                        />

                        <Field
                            label="Porta interna"
                            type="number"
                            min={1}
                            max={65535}
                            step={1}
                            value={internalPort}
                            error={internalPort && !isValidPort(internalPort) ? 'Porta inválida.' : ''}
                            inputMode="numeric"
                            placeholder="5900"
                            onChange={(value) => setInternalPort(value.replace(/\D/g, ''))}
                        />

                        <Field
                            label="Zona origem"
                            value={srcZone}
                            placeholder="vpn"
                            onChange={setSrcZone}
                        />

                        <Field
                            label="Zona destino"
                            value={destZone}
                            placeholder="lan"
                            onChange={setDestZone}
                        />
                    </div>

                    <div className="rounded-2xl border border-white/10 bg-black/20 p-4">
                        <p className="text-xs font-bold uppercase tracking-[0.16em] text-slate-500">
                            Preview
                        </p>

                        <p className="mt-2 font-mono text-sm text-slate-300">
                            {externalPort || '—'} → {internalIp || '—'}:{internalPort || '—'} · {proto || 'tcp'}
                        </p>
                    </div>
                </div>

                <div className="flex items-center justify-end gap-3 border-t border-white/10 px-6 py-4">
                    <Button
                        type="button"
                        variant="secondary"
                        onClick={onClose}
                        className="rounded-2xl px-4 py-3 font-bold"
                    >
                        Cancelar
                    </Button>

                    <Button
                        type="button"
                        onClick={saveRule}
                        disabled={!formIsValid || saving}
                        className="rounded-2xl px-5 py-3 font-black disabled:bg-slate-700 disabled:text-slate-400"
                    >
                        <Save size={16} />
                        {saving ? 'A guardar...' : 'Guardar regra'}
                    </Button>
                </div>
            </div>
        </div>
    );
}

function DeleteRuleModal({
    rule,
    loading,
    onClose,
    onConfirm,
}: {
    rule: RouterFirewallRule;
    loading: boolean;
    onClose: () => void;
    onConfirm: () => Promise<void> | void;
}) {
    return (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm">
            <div className="w-full max-w-md overflow-hidden rounded-3xl border border-white/10 bg-[#050b1a] shadow-2xl">
                <div className="border-b border-white/10 px-6 py-5">
                    <p className="text-xs font-bold uppercase tracking-[0.18em] text-red-300/80">
                        Apagar Regra
                    </p>

                    <h2 className="mt-2 text-xl font-black text-white">
                        Tem a certeza?
                    </h2>

                    <p className="mt-2 text-sm text-slate-400">
                        Vai apagar a regra{' '}
                        <span className="font-bold text-white">
                            {rule.name || rule.id}
                        </span>
                        .
                    </p>

                    <p className="mt-3 font-mono text-xs text-slate-500">
                        {rule.externalPort} → {rule.internalIp}:{rule.internalPort}
                    </p>
                </div>

                <div className="flex justify-end gap-3 px-6 py-4">
                    <Button
                        type="button"
                        variant="secondary"
                        onClick={onClose}
                        className="rounded-2xl px-4 py-3 font-bold"
                    >
                        Cancelar
                    </Button>

                    <Button
                        type="button"
                        variant="danger"
                        disabled={loading}
                        onClick={onConfirm}
                        className="rounded-2xl px-5 py-3 font-black"
                    >
                        <Trash2 size={16} />
                        {loading ? 'A apagar...' : 'Apagar'}
                    </Button>
                </div>
            </div>
        </div>
    );
}

function parseClientTxt(content: string): {
    password: string;
    controllers: EditableController[];
} {
    const lines = content
        .replace(/\r\n/g, '\n')
        .replace(/\r/g, '\n')
        .split('\n')
        .filter((line) => line.length > 0);

    const password = lines[0] ?? '';

    const controllers = lines.slice(1).map((line, index) => {
        const [name = '', routerVpnIp = '', externalPort = '', password = ''] =
            line.split('xxx');

        return {
            id: crypto.randomUUID(),
            name,
            routerVpnIp,
            externalPort,
            password,
        };
    });

    return {
        password,
        controllers,
    };
}

function Field({
    label,
    value,
    onChange,
    error,
    inputMode,
    maxLength,
    type = 'text',
    min,
    max,
    step,
    hideLabel = false,
    placeholder,
    autoFocus = false,
    disabled = false
}: {
    label: string;
    value: string;
    onChange: (value: string) => void;
    error?: string;
    inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];
    maxLength?: number;
    type?: React.HTMLInputTypeAttribute;
    min?: number;
    max?: number;
    step?: number;
    hideLabel?: boolean;
    placeholder?: string;
    autoFocus?: boolean;
    disabled?: boolean;
}) {
    return (
        <div>
            {!hideLabel && (
                <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                    {label}
                </label>
            )}

            <div className="relative">
                <input
                    type={type}
                    value={value}
                    autoFocus={autoFocus}
                    min={min}
                    max={max}
                    disabled={disabled}
                    placeholder={placeholder}
                    step={step}
                    inputMode={inputMode}
                    maxLength={maxLength}
                    onChange={(event) => onChange(event.target.value)}
                    className={`w-full rounded-xl border bg-slate-950 px-3 py-3 pr-12 font-mono text-sm text-white outline-none transition placeholder:text-slate-600 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ${error
                        ? 'border-red-500/60 focus:border-red-400'
                        : 'border-white/10 focus:border-blue-500/40'
                        }`}
                />

                {type === 'number' && (
                    <div className="absolute right-2 top-1/2 flex -translate-y-1/2 flex-col gap-1">
                        <button
                            type="button"
                            onClick={() => {
                                const current = Number(value || 0);
                                const next = current + Number(step || 1);

                                if (typeof max === 'number' && next > max) return;

                                onChange(String(next));
                            }}
                            className="flex h-4 w-4 items-center justify-center rounded bg-white/[0.06] text-[10px] text-slate-300 transition hover:bg-blue-500/20 hover:text-white"
                        >
                            +
                        </button>

                        <button
                            type="button"
                            onClick={() => {
                                const current = Number(value || 0);
                                const next = current - Number(step || 1);

                                const floor = typeof min === 'number' ? min : 5900;

                                onChange(String(Math.max(floor, next)));
                            }}
                            className="flex h-4 w-4 items-center justify-center rounded bg-white/[0.06] text-[10px] text-slate-300 transition hover:bg-blue-500/20 hover:text-white"
                        >
                            −
                        </button>
                    </div>
                )}
            </div>

            {error && (
                <p className="mt-2 text-xs font-medium text-red-300">
                    {error}
                </p>
            )}
        </div>
    );
}

function IpField({
    label,
    value,
    onChange,
    error,
    hideLabel = false,
    autoFocusFirstOctet = false,
    autoFocusOctetIndex = 0,
}: {
    label: string;
    value: string;
    onChange: (value: string) => void;
    error?: string;
    hideLabel?: boolean;
    autoFocusFirstOctet?: boolean;
    autoFocusOctetIndex?: number;
}) {
    const inputRefs = useRef<Array<HTMLInputElement | null>>([]);

    useEffect(() => {
        if (!autoFocusFirstOctet) return;

        requestAnimationFrame(() => {
            const input = inputRefs.current[autoFocusOctetIndex];

            input?.focus();
            input?.setSelectionRange(input.value.length, input.value.length);
        });
    }, [autoFocusFirstOctet, autoFocusOctetIndex]);

    const parts = value.split('.');
    const octets = Array.from({ length: 4 }, (_, index) => parts[index] ?? '');

    function updateOctet(index: number, rawValue: string) {
        const nextValue = rawValue.replace(/\D/g, '').slice(0, 3);

        if (nextValue && Number(nextValue) > 255) return;

        const nextOctets = [...octets];
        nextOctets[index] = nextValue;

        onChange(nextOctets.join('.'));
    }

    function handleKeyDown(
        index: number,
        event: React.KeyboardEvent<HTMLInputElement>,
    ) {
        if (event.key === '.') {
            event.preventDefault();

            inputRefs.current[index + 1]?.focus();

            return;
        }

        if (event.key !== 'Backspace') return;

        const current = octets[index];

        if (current.length > 0) {
            const nextOctets = [...octets];
            nextOctets[index] = current.slice(0, -1);

            onChange(nextOctets.join('.'));
            event.preventDefault();

            return;
        }

        if (index > 0) {
            event.preventDefault();
            inputRefs.current[index - 1]?.focus();
        }
    }

    return (
        <div>
            {!hideLabel && (
                <label className="mb-2 block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                    {label}
                </label>
            )}

            <div
                className={`flex items-center rounded-xl border bg-slate-950 px-3 py-2 font-mono text-sm text-white transition ${error
                    ? 'border-red-500/60 focus-within:border-red-400'
                    : 'border-white/10 focus-within:border-blue-500/40'
                    }`}
            >
                {octets.map((octet, index) => (
                    <Fragment key={index}>
                        <input
                            ref={(element) => {
                                inputRefs.current[index] = element;
                            }}
                            value={octet}
                            inputMode="numeric"
                            maxLength={3}
                            onChange={(event) =>
                                updateOctet(index, event.target.value)
                            }
                            onKeyDown={(event) =>
                                handleKeyDown(index, event)
                            }
                            className="w-full min-w-0 bg-transparent px-1 py-1 text-center outline-none"
                        />

                        {index < 3 && (
                            <span className="select-none px-1 text-slate-500">
                                .
                            </span>
                        )}
                    </Fragment>
                ))}
            </div>

            {error && (
                <p className="mt-2 text-xs font-medium text-red-300">
                    {error}
                </p>
            )}
        </div>
    );
}

type FieldErrors = Partial<Record<keyof EditableController, string>>;

const IPV4_REGEX =
    /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;

const HOSTNAME_REGEX =
    /^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;

function isValidRouterHost(value: string) {
    const trimmed = value.trim();

    return IPV4_REGEX.test(trimmed) || HOSTNAME_REGEX.test(trimmed);
}

const INVALID_CONTROLLER_NAME_REGEX = /(xxx|\r|\n)/i;

function hasTxtSeparator(value: string) {
    return value.includes('xxx') || value.includes('\n') || value.includes('\r');
}

function validatePort(value: string) {
    const port = Number(value);

    if (!value.trim()) {
        return 'A porta externa é obrigatória.';
    }

    if (!/^\d+$/.test(value)) {
        return 'A porta deve conter apenas números.';
    }

    if (port < 5900 || port > 5999) {
        return 'A porta deve estar entre 5900 e 5999.';
    }

    return '';
}

function isValidPort(value: string) {
    const port = Number(value);

    return /^\d+$/.test(value) && port >= 1 && port <= 65535;
}

function getNextAvailablePort(controllers: EditableController[]) {
    const usedPorts = new Set(
        controllers
            .map((controller) => Number(controller.externalPort))
            .filter((port) => Number.isInteger(port) && port >= 5900),
    );

    let port = 5900;

    while (usedPorts.has(port)) {
        port += 1;
    }

    return String(port);
}

function validateController(controller: EditableController): FieldErrors {
    const errors: FieldErrors = {};

    if (!controller.name.trim()) {
        errors.name = 'O nome é obrigatório.';
    } else if (INVALID_CONTROLLER_NAME_REGEX.test(controller.name)) {
        errors.name =
            'O nome não pode conter "xxx" ou quebras de linha.';
    }

    if (!controller.routerVpnIp.trim()) {
        errors.routerVpnIp = 'O router/host é obrigatório.';
    } else if (!isValidRouterHost(controller.routerVpnIp)) {
        errors.routerVpnIp = 'Introduz um IPv4 ou hostname válido.';
    }

    const portError = validatePort(controller.externalPort);
    if (portError) errors.externalPort = portError;

    if (!/^\d{6}$/.test(controller.password)) {
        errors.password = 'O PIN/password deve ter exatamente 6 dígitos.';
    }

    return errors;
}

function hasErrors(errors: FieldErrors) {
    return Object.values(errors).some(Boolean);
}

type RouterFieldMode = 'host' | 'ip';

function SmartRouterField({
    label,
    value,
    onChange,
    error,
}: {
    label: string;
    value: string;
    onChange: (value: string) => void;
    error?: string;
}) {
    const [mode, setMode] = useState<RouterFieldMode>(() =>
        IPV4_REGEX.test(value.trim()) ? 'ip' : 'host',
    );

    const [focusOctetIndex, setFocusOctetIndex] = useState(0);
    const [isFocused, setIsFocused] = useState(false);

    useEffect(() => {
        if (!value.trim()) {
            setMode('host');
            setFocusOctetIndex(0);
        }
    }, [value]);

    function handleHostChange(nextValue: string) {
        const shouldSwitchToIp =
            /^[\d.]+$/.test(nextValue) && /\d+\.$/.test(nextValue);

        if (shouldSwitchToIp) {
            setFocusOctetIndex(
                Math.min(nextValue.split('.').length - 1, 3),
            );

            setMode('ip');
        }

        onChange(nextValue);
    }

    function handleIpChange(nextValue: string) {
        if (!nextValue.replace(/\./g, '').trim()) {
            onChange('');
            return;
        }

        onChange(nextValue);
    }
    return (
        <div
            onFocus={() => setIsFocused(true)}
            onBlur={() => {
                setIsFocused(false);

                if (!value.trim()) {
                    setMode('host');
                    setFocusOctetIndex(0);
                }
            }}
        >
            <div className="mb-2 flex items-center justify-between gap-3">
                <label className="block text-xs font-medium uppercase tracking-[0.16em] text-slate-500">
                    {label}
                </label>

                <span className="rounded-xl border border-white/10 bg-slate-950 px-2.5 py-1 text-[10px] font-black uppercase tracking-[0.12em] text-blue-200">
                    {mode === 'ip' ? 'Auto · IPv4' : 'Auto · Host'}
                </span>
            </div>

            {mode === 'ip' ? (
                <IpField
                    label=""
                    value={value}
                    error={error}
                    onChange={handleIpChange}
                    hideLabel
                    autoFocusFirstOctet
                    autoFocusOctetIndex={focusOctetIndex}
                />
            ) : (
                <Field
                    label=""
                    value={value}
                    error={error}
                    onChange={handleHostChange}
                    placeholder="198.19.0.167 ou adoraflor.nsupdate.info"
                    hideLabel
                    autoFocus
                />
            )}
        </div>
    );
}

function generateClientPassword() {
    const alphabet =
        'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';

    return Array.from({ length: 8 }, () =>
        alphabet[Math.floor(Math.random() * alphabet.length)],
    ).join('');
}

