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

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

import type { ControllerClient } 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);

    async function loadClients() {
        try {
            setLoading(true);
            setError('');

            const response = await vpsApi.listControllerClients();

            setClients(response.clients);
            setSelectedId((current) => 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];

    return (
        <div className="flex h-full min-h-0 flex-col overflow-hidden">
            <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"
                    onClick={loadClients}
                    disabled={loading}
                    className="inline-flex shrink-0 items-center gap-2 rounded-2xl border border-blue-500/20 bg-blue-500/15 px-4 py-3 text-sm font-bold text-blue-100 transition hover:bg-blue-500/20 disabled:cursor-not-allowed disabled:opacity-50"
                >
                    <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 min-h-0 flex-1 gap-4 overflow-hidden xl:grid-cols-12">
                <Card className="flex min-h-0 flex-col overflow-hidden xl:col-span-5">
                    <div className="mb-4 flex shrink-0 items-center justify-between gap-3">
                        <div>
                            <h3 className="font-bold text-white">
                                Clientes
                            </h3>

                            <p className="text-xs text-slate-500">
                                Ficheiros em /var/litoral_regas_app/maquinas
                            </p>
                        </div>

                        <div className="rounded-2xl bg-white/[0.03] px-3 py-2 text-xs font-bold text-slate-300">
                            {filteredClients.length} / {clients.length} clientes
                        </div>
                    </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={`min-h-0 flex-1 space-y-2 overflow-y-auto pr-2 ${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={`w-full rounded-2xl border p-4 text-left transition ${active
                                        ? 'border-blue-500/25 bg-blue-500/10'
                                        : 'border-white/10 bg-white/[0.025] hover:bg-white/[0.045]'
                                        }`}
                                >
                                    <div className="flex items-start justify-between gap-3">
                                        <div className="min-w-0">
                                            <p className="truncate font-bold text-white">
                                                {client.name}
                                            </p>

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

                                        <span
                                            className={`rounded-xl px-2.5 py-1 text-xs font-bold ${client.controller_count > 0
                                                ? 'bg-blue-500/10 text-blue-200'
                                                : 'bg-white/[0.04] text-slate-400'
                                                }`}
                                        >
                                            {client.controller_count}
                                        </span>
                                    </div>

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

                                        <span>
                                            {client.controller_count > 0
                                                ? 'Com controladores'
                                                : 'Sem controladores'}
                                        </span>
                                    </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"
                                disabled={page <= 1}
                                onClick={() =>
                                    setPage((current) =>
                                        Math.max(1, current - 1),
                                    )
                                }
                                className="rounded-xl border border-white/10 px-3 py-2 font-bold text-slate-300 transition hover:bg-white/[0.04] disabled:cursor-not-allowed disabled:opacity-40"
                            >
                                Anterior
                            </button>

                            <button
                                type="button"
                                disabled={page >= totalPages}
                                onClick={() =>
                                    setPage((current) =>
                                        Math.min(totalPages, current + 1),
                                    )
                                }
                                className="rounded-xl border border-white/10 px-3 py-2 font-bold text-slate-300 transition hover:bg-white/[0.04] disabled:cursor-not-allowed disabled:opacity-40"
                            >
                                Seguinte
                            </button>
                        </div>
                    </div>
                </Card>

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

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

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

                                    <p className="mt-2 text-xs text-slate-500">
                                        Última alteração:{' '}
                                        {selectedClient.modified_at ?? '—'}
                                    </p>
                                </div>

                                <button
                                    type="button"
                                    onClick={() =>
                                        setEditingClient(selectedClient)
                                    }
                                    className="inline-flex items-center gap-2 rounded-2xl bg-blue-500 px-4 py-3 text-sm font-black text-white transition hover:bg-blue-400"
                                >
                                    <Pencil size={17} />
                                    Editar Cliente
                                </button>
                            </div>

                            <div className="grid gap-3 md:grid-cols-2">
                                <MiniStat
                                    icon={<Monitor size={18} />}
                                    label="Controladores"
                                    value={String(
                                        selectedClient.controller_count,
                                    )}
                                />

                                <MiniStat
                                    icon={<Server size={18} />}
                                    label="Ficheiro VPS"
                                    value={selectedClient.file}
                                />
                            </div>

                            <div className="mt-5 rounded-2xl border border-white/10 bg-white/[0.025] p-4">
                                <h4 className="font-bold text-white">
                                    Próximas ações
                                </h4>

                                <div className="mt-4 space-y-3 text-sm text-slate-400">
                                    <p>• Abrir modal de edição do cliente.</p>
                                    <p>
                                        • Listar controladores do ficheiro .txt.
                                    </p>
                                    <p>
                                        • Detetar router associado pelo IP
                                        WireGuard.
                                    </p>
                                    <p>
                                        • Adicionar, atualizar ou remover DNAT no
                                        router.
                                    </p>
                                    <p>
                                        • Atualizar entrada correspondente no
                                        ficheiro VPS.
                                    </p>
                                </div>
                            </div>
                        </div>
                    ) : (
                        <div className="flex h-full items-center justify-center text-slate-500">
                            Nenhum cliente selecionado.
                        </div>
                    )}
                </Card>
            </div>

            {editingClient && (
                <EditClientModal
                    client={editingClient}
                    onClose={() => setEditingClient(null)}
                />
            )}
        </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 EditClientModal({
    client,
    onClose,
}: {
    client: ControllerClient;
    onClose: () => void;
}) {
    const [clientPassword, setClientPassword] = useState('');
    const [controllers, setControllers] = useState<EditableController[]>([]);
    const [loadingClientFile, setLoadingClientFile] = useState(true);
    const [clientFileError, setClientFileError] = 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),
        );
    }

    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>

                {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 p-6 xl:grid-cols-12">
                        <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"
                                        onClick={addController}
                                        className="inline-flex items-center gap-2 rounded-2xl border border-blue-500/20 bg-blue-500/15 px-4 py-3 text-sm font-bold text-blue-100 transition 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(
                                                                /[^A-Za-z0-9_-]/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"
                            onClick={onClose}
                            className="rounded-2xl border border-white/10 px-4 py-3 text-sm font-bold text-slate-300 transition hover:bg-white/[0.04]"
                        >
                            Cancelar
                        </button>

                        <button
                            type="button"
                            disabled={!formIsValid}
                            className="inline-flex items-center gap-2 rounded-2xl bg-blue-500 px-5 py-3 text-sm font-black text-white transition hover:bg-blue-400 disabled:cursor-not-allowed disabled:bg-slate-700 disabled:text-slate-400"
                        >
                            <Save size={16} />
                            Guardar alterações
                        </button>
                    </div>
                </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
}: {
    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;
}) {
    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}
                    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 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>
    );
}