import { Button, Card, Input, Label, Radio, Select } from '@/Components/ui';
import { AlertCircle, CheckCircle2, ChevronDown, ChevronRight, Loader2 } from 'lucide-react';

function Code({ children }: { children: ReactNode }) {
    return (
        <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
            {children}
        </code>
    );
}

function CodeBlock({ children }: { children: string }) {
    return (
        <pre className="overflow-x-auto rounded-md bg-muted px-3 py-2.5 font-mono text-[11px] leading-relaxed text-foreground">
            {children.trim()}
        </pre>
    );
}

function SetupGuide() {
    const [open, setOpen] = useState(false);
    const Chevron = open ? ChevronDown : ChevronRight;
    return (
        <div className="rounded-lg border border-border">
            <button
                type="button"
                onClick={() => setOpen((v) => !v)}
                className="flex w-full items-center gap-2 px-3 py-2.5 text-left text-[12px] font-medium text-foreground hover:bg-muted/40"
            >
                <Chevron className="h-3.5 w-3.5 shrink-0 text-muted-foreground" strokeWidth={2} />
                Setup guide
            </button>
            {open && (
                <div className="space-y-4 border-t border-border px-4 py-4 text-[12px] text-muted-foreground">

                    {/* Step 1 */}
                    <div className="space-y-1.5">
                        <p className="font-medium text-foreground">1. Generate credentials</p>
                        <p>Run this on your server to generate the three credential values:</p>
                        <CodeBlock>php artisan reverb:install</CodeBlock>
                        <p>
                            Or generate manually — any random strings work. Copy the{' '}
                            <Code>APP_ID</Code>, <Code>APP_KEY</Code>, and <Code>APP_SECRET</Code>{' '}
                            values into the fields above and paste them into your{' '}
                            <Code>.env</Code> as well.
                        </p>
                        <CodeBlock>{`REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
BROADCAST_CONNECTION=reverb`}</CodeBlock>
                    </div>

                    {/* Step 2 */}
                    <div className="space-y-1.5">
                        <p className="font-medium text-foreground">2. Start Reverb</p>
                        <p className="font-medium text-foreground/70">Development</p>
                        <CodeBlock>php artisan reverb:start --host=127.0.0.1 --port=8080</CodeBlock>
                        <p className="font-medium text-foreground/70">Production — Supervisor</p>
                        <p>
                            Create <Code>/etc/supervisor/conf.d/reverb.conf</Code>:
                        </p>
                        <CodeBlock>{`[program:reverb]
command=php /path/to/app/artisan reverb:start --host=0.0.0.0 --port=8080
directory=/path/to/app
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/path/to/app/storage/logs/reverb.log`}</CodeBlock>
                        <CodeBlock>{`sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start reverb`}</CodeBlock>
                    </div>

                    {/* Step 3 */}
                    <div className="space-y-1.5">
                        <p className="font-medium text-foreground">3. Web server — proxy WebSocket traffic</p>
                        <p>
                            Set <strong className="font-medium text-foreground">Host</strong> above to
                            your domain and <strong className="font-medium text-foreground">Scheme</strong>{' '}
                            to <Code>https</Code> — the web server handles TLS, Reverb runs plain HTTP
                            internally on port 8080.
                        </p>

                        <p className="font-medium text-foreground/70">Nginx</p>
                        <p>Add inside your <Code>server {'{}'}</Code> block:</p>
                        <CodeBlock>{`location /app {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 60s;
}

location /apps {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
}`}</CodeBlock>
                        <CodeBlock>sudo nginx -s reload</CodeBlock>

                        <p className="font-medium text-foreground/70">Apache</p>
                        <p>
                            Enable the required modules first, then add inside your{' '}
                            <Code>{'<VirtualHost>'}</Code>:
                        </p>
                        <CodeBlock>sudo a2enmod proxy proxy_http proxy_wstunnel rewrite</CodeBlock>
                        <CodeBlock>{`RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/app/(.*) ws://127.0.0.1:8080/app/$1 [P,L]

ProxyPass /app/ http://127.0.0.1:8080/app/
ProxyPassReverse /app/ http://127.0.0.1:8080/app/
ProxyPass /apps/ http://127.0.0.1:8080/apps/
ProxyPassReverse /apps/ http://127.0.0.1:8080/apps/`}</CodeBlock>
                        <CodeBlock>sudo systemctl restart apache2</CodeBlock>
                    </div>

                    {/* Step 4 */}
                    <div className="space-y-1.5">
                        <p className="font-medium text-foreground">4. Field reference</p>
                        <table className="w-full border-collapse text-[11px]">
                            <tbody>
                                {[
                                    ['Host', 'Your domain (no scheme). Nginx proxies WS traffic to Reverb on port 8080 internally.'],
                                    ['Port', '443 on production (Nginx SSL). 8080 on local dev (direct).'],
                                    ['Scheme', 'https on production (Nginx handles TLS). http on local dev.'],
                                    ['App ID', 'Matches REVERB_APP_ID in .env — used in the URL path.'],
                                    ['Key', 'Matches REVERB_APP_KEY — public identifier sent by the browser.'],
                                    ['Secret', 'Matches REVERB_APP_SECRET — used to sign server-side events. Never exposed to browsers.'],
                                ].map(([field, desc]) => (
                                    <tr key={field} className="border-b border-border/50 last:border-0">
                                        <td className="py-1.5 pr-3 font-medium text-foreground/80 align-top whitespace-nowrap">
                                            {field}
                                        </td>
                                        <td className="py-1.5 text-muted-foreground">{desc}</td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                </div>
            )}
        </div>
    );
}
import { type ReactNode, useState } from 'react';
import { csrfHeaders } from './types';

/**
 * Realtime collab transport (Team Workspaces P6 / Tier 2 — Reverb opt-in,
 * Δ2/Δ21). DEFAULT 'lock' (pessimistic project lock, both modes,
 * shared-hosting-safe). 'reverb' is an explicit SSH/VPS opt-in that needs a
 * running `php artisan reverb:start` process — never the shared default.
 *
 * Self-contained save (its own PATCH endpoint), so it doesn't interleave with
 * the global Settings form. The secret is never echoed back (`has_secret`
 * boolean); a blank submit leaves it untouched.
 */

export type RealtimePayload = {
    transport: 'lock' | 'reverb';
    reverb_editing: 'locked' | 'concurrent';
    reverb_host: string;
    reverb_port: number | null;
    reverb_scheme: 'http' | 'https';
    reverb_app_id: string;
    reverb_key: string;
    has_secret: boolean;
};

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

type Props = {
    realtime: RealtimePayload;
};

export default function RealtimeTab({ realtime }: Props) {
    const [transport, setTransport] = useState<'lock' | 'reverb'>(realtime.transport);
    const [editing, setEditing] = useState<'locked' | 'concurrent'>(
        realtime.reverb_editing,
    );
    const [host, setHost] = useState(realtime.reverb_host);
    const [port, setPort] = useState(
        realtime.reverb_port != null ? String(realtime.reverb_port) : '',
    );
    const [scheme, setScheme] = useState<'http' | 'https'>(realtime.reverb_scheme);
    const [appId, setAppId] = useState(realtime.reverb_app_id);
    const [key, setKey] = useState(realtime.reverb_key);
    const [secret, setSecret] = useState('');
    const [editingSecret, setEditingSecret] = useState(!realtime.has_secret);

    const [saving, setSaving] = useState(false);
    const [savedAt, setSavedAt] = useState<number | null>(null);
    const [errors, setErrors] = useState<FieldErrors>({});
    const [submitError, setSubmitError] = useState<string | null>(null);

    const [testing, setTesting] = useState(false);
    const [testResult, setTestResult] = useState<{ ok: boolean; message?: string } | null>(null);

    const testConnection = async () => {
        setTesting(true);
        setTestResult(null);
        try {
            const res = await fetch('/admin/settings/realtime/test', {
                method: 'POST',
                credentials: 'same-origin',
                headers: csrfHeaders(),
            });
            const body = await res.json().catch(() => null);
            setTestResult({ ok: body?.ok === true, message: body?.message });
        } catch {
            setTestResult({ ok: false, message: 'Network error. Try again.' });
        } finally {
            setTesting(false);
        }
    };

    const reverbSelected = transport === 'reverb';

    const submit = async () => {
        setSaving(true);
        setErrors({});
        setSubmitError(null);
        try {
            const res = await fetch('/admin/settings/realtime', {
                method: 'PATCH',
                credentials: 'same-origin',
                headers: csrfHeaders(),
                body: JSON.stringify({
                    transport,
                    reverb_editing: editing,
                    reverb_host: host || null,
                    reverb_port: port.trim() === '' ? null : Number.parseInt(port, 10),
                    reverb_scheme: scheme,
                    reverb_app_id: appId || null,
                    reverb_key: key || null,
                    // Only send the secret when the admin typed a replacement.
                    reverb_secret: editingSecret && secret !== '' ? secret : '',
                }),
            });

            if (res.status === 422) {
                const body = await res.json().catch(() => null);
                const fieldErrors: FieldErrors = {};
                for (const [k, msgs] of Object.entries(body?.errors ?? {})) {
                    if (Array.isArray(msgs) && msgs.length > 0) {
                        fieldErrors[k] = msgs[0] as string;
                    }
                }
                setErrors(fieldErrors);
                return;
            }
            if (!res.ok) {
                const body = await res.json().catch(() => null);
                setSubmitError(body?.message ?? `Save failed (${res.status}).`);
                return;
            }
            setSecret('');
            setEditingSecret(false);
            setSavedAt(Date.now());
            window.setTimeout(() => setSavedAt(null), 2400);
        } catch {
            setSubmitError('Network error. Try again.');
        } finally {
            setSaving(false);
        }
    };

    return (
        <Card padding="lg">
            <header className="mb-5">
                <h2 className="text-base font-semibold tracking-tight text-foreground">
                    Realtime editing
                </h2>
                <p className="mt-1 text-[12px] text-muted-foreground">
                    How editors share a project. The default keeps one editor at a
                    time (with take-over) and works on any host. Reverb adds live
                    presence + instant sync — and an optional concurrent mode — but{' '}
                    <strong className="font-semibold text-foreground">
                        requires a running{' '}
                        <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">
                            php artisan reverb:start
                        </code>{' '}
                        process
                    </strong>{' '}
                    — SSH / VPS only, not shared hosting.
                </p>
            </header>

            <fieldset className="space-y-2.5">
                <legend className="mb-1 text-[12px] font-medium text-foreground">
                    Transport
                </legend>
                {(
                    [
                        {
                            key: 'lock',
                            label: 'Single editor lock (default)',
                            description:
                                'One editor at a time; others are read-only and can take over. No websocket — works everywhere.',
                        },
                        {
                            key: 'reverb',
                            label: 'Reverb realtime (opt-in)',
                            description:
                                'Live presence + instant sync via a Reverb websocket. Choose locked or concurrent editing below. Needs a persistent reverb:start process.',
                        },
                    ] as const
                ).map((opt) => {
                    const checked = transport === opt.key;
                    return (
                        <label
                            key={opt.key}
                            className={
                                'flex cursor-pointer items-start gap-3 rounded-lg border px-3 py-2.5 transition-colors ' +
                                (checked
                                    ? 'border-accent/50 bg-accent/5'
                                    : 'border-border bg-background hover:bg-muted/40')
                            }
                            data-testid={`realtime-transport-${opt.key}`}
                        >
                            <Radio
                                name="collab_transport"
                                value={opt.key}
                                checked={checked}
                                onChange={() => setTransport(opt.key)}
                                wrapperClassName="mt-0.5"
                            />
                            <div className="min-w-0 flex-1">
                                <p className="text-[13px] font-medium text-foreground">
                                    {opt.label}
                                </p>
                                <p className="mt-0.5 text-[12px] text-muted-foreground">
                                    {opt.description}
                                </p>
                            </div>
                        </label>
                    );
                })}
            </fieldset>

            {reverbSelected && (
                <div
                    className="mt-5 space-y-4 rounded-lg border border-border bg-background/40 p-4"
                    data-testid="realtime-reverb-fields"
                >
                    <fieldset className="space-y-2.5">
                        <legend className="mb-1 text-[12px] font-medium text-foreground">
                            Editing model
                        </legend>
                        {(
                            [
                                {
                                    key: 'locked',
                                    label: 'Locked — one editor at a time (recommended)',
                                    description:
                                        'Single editor with instant take-over; everyone sees live presence and changes. Safe — no risk of edits overwriting each other.',
                                },
                                {
                                    key: 'concurrent',
                                    label: 'Concurrent — everyone edits at once',
                                    description:
                                        'All editors can edit simultaneously. Saves are last-write-wins per screen, so two people editing the same screen can overwrite each other.',
                                },
                            ] as const
                        ).map((opt) => {
                            const checked = editing === opt.key;
                            return (
                                <label
                                    key={opt.key}
                                    className={
                                        'flex cursor-pointer items-start gap-3 rounded-lg border px-3 py-2.5 transition-colors ' +
                                        (checked
                                            ? 'border-accent/50 bg-accent/5'
                                            : 'border-border bg-background hover:bg-muted/40')
                                    }
                                    data-testid={`realtime-editing-${opt.key}`}
                                >
                                    <Radio
                                        name="collab_editing"
                                        value={opt.key}
                                        checked={checked}
                                        onChange={() => setEditing(opt.key)}
                                        wrapperClassName="mt-0.5"
                                    />
                                    <div className="min-w-0 flex-1">
                                        <p className="text-[13px] font-medium text-foreground">
                                            {opt.label}
                                        </p>
                                        <p className="mt-0.5 text-[12px] text-muted-foreground">
                                            {opt.description}
                                        </p>
                                    </div>
                                </label>
                            );
                        })}
                    </fieldset>

                    <div className="grid gap-4 sm:grid-cols-2">
                        <div className="space-y-1.5">
                            <Label htmlFor="reverb_host">Host</Label>
                            <Input
                                id="reverb_host"
                                value={host}
                                onChange={(e) => setHost(e.target.value)}
                                invalid={Boolean(errors.reverb_host)}
                                placeholder="ws.example.com"
                            />
                            {errors.reverb_host && (
                                <p className="text-xs text-destructive">{errors.reverb_host}</p>
                            )}
                        </div>
                        <div className="space-y-1.5">
                            <Label htmlFor="reverb_port">Port</Label>
                            <Input
                                id="reverb_port"
                                type="number"
                                value={port}
                                onChange={(e) => setPort(e.target.value)}
                                invalid={Boolean(errors.reverb_port)}
                                placeholder="443"
                            />
                            {errors.reverb_port && (
                                <p className="text-xs text-destructive">{errors.reverb_port}</p>
                            )}
                        </div>
                    </div>

                    <div className="grid gap-4 sm:grid-cols-2">
                        <div className="space-y-1.5">
                            <Label htmlFor="reverb_scheme">Scheme</Label>
                            <Select
                                id="reverb_scheme"
                                value={scheme}
                                onChange={(e) => setScheme(e.target.value as 'http' | 'https')}
                            >
                                <option value="https">https / wss (TLS)</option>
                                <option value="http">http / ws (no TLS)</option>
                            </Select>
                        </div>
                        <div className="space-y-1.5">
                            <Label htmlFor="reverb_app_id">App ID</Label>
                            <Input
                                id="reverb_app_id"
                                value={appId}
                                onChange={(e) => setAppId(e.target.value)}
                                invalid={Boolean(errors.reverb_app_id)}
                                placeholder="appshot"
                            />
                        </div>
                    </div>

                    <div className="space-y-1.5">
                        <Label htmlFor="reverb_key">Key</Label>
                        <Input
                            id="reverb_key"
                            value={key}
                            onChange={(e) => setKey(e.target.value)}
                            invalid={Boolean(errors.reverb_key)}
                            placeholder="public app key"
                        />
                    </div>

                    <div className="space-y-1.5">
                        <Label htmlFor="reverb_secret">Secret</Label>
                        {editingSecret ? (
                            <Input
                                id="reverb_secret"
                                type="password"
                                value={secret}
                                onChange={(e) => setSecret(e.target.value)}
                                invalid={Boolean(errors.reverb_secret)}
                                placeholder="app secret"
                                autoComplete="new-password"
                            />
                        ) : (
                            <div className="flex items-center gap-3">
                                <span className="inline-flex items-center gap-1.5 text-[12px] font-medium text-success">
                                    <CheckCircle2 className="h-3.5 w-3.5" strokeWidth={2.5} />
                                    Configured
                                </span>
                                <button
                                    type="button"
                                    className="text-[12px] font-medium text-accent hover:underline"
                                    onClick={() => setEditingSecret(true)}
                                >
                                    Replace
                                </button>
                            </div>
                        )}
                        <p className="text-xs text-muted-foreground">
                            Stored encrypted. Match your{' '}
                            <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">
                                REVERB_APP_SECRET
                            </code>
                            .
                        </p>
                    </div>

                    <SetupGuide />
                </div>
            )}

            {submitError && (
                <div
                    className="mt-4 flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-[12px] font-medium text-destructive"
                    role="alert"
                >
                    <AlertCircle className="mt-0.5 h-3.5 w-3.5" strokeWidth={2.5} />
                    <span>{submitError}</span>
                </div>
            )}

            <div className="mt-5 flex flex-wrap items-center gap-3">
                <Button
                    type="button"
                    variant="primary"
                    size="md"
                    disabled={saving}
                    onClick={submit}
                    data-testid="realtime-save"
                >
                    {saving ? (
                        <Loader2 className="h-3.5 w-3.5 animate-spin" strokeWidth={2} />
                    ) : null}
                    {saving ? 'Saving…' : 'Save changes'}
                </Button>
                {reverbSelected && realtime.has_secret && (
                    <Button
                        type="button"
                        variant="secondary"
                        size="md"
                        disabled={testing || saving}
                        onClick={testConnection}
                        data-testid="realtime-test"
                    >
                        {testing ? (
                            <Loader2 className="h-3.5 w-3.5 animate-spin" strokeWidth={2} />
                        ) : null}
                        {testing ? 'Testing…' : 'Test connection'}
                    </Button>
                )}
                {savedAt !== null && (
                    <span
                        className="inline-flex items-center gap-1.5 text-[12px] font-medium text-success"
                        data-testid="realtime-saved"
                    >
                        <CheckCircle2 className="h-3.5 w-3.5" strokeWidth={2.5} />
                        Saved
                    </span>
                )}
                {testResult !== null && (
                    <span
                        className={
                            'inline-flex items-center gap-1.5 text-[12px] font-medium ' +
                            (testResult.ok ? 'text-success' : 'text-destructive')
                        }
                        data-testid="realtime-test-result"
                    >
                        {testResult.ok ? (
                            <CheckCircle2 className="h-3.5 w-3.5" strokeWidth={2.5} />
                        ) : (
                            <AlertCircle className="h-3.5 w-3.5" strokeWidth={2.5} />
                        )}
                        {testResult.ok
                            ? 'Connected — Reverb is reachable and credentials are valid.'
                            : (testResult.message ?? 'Connection failed.')}
                    </span>
                )}
            </div>
        </Card>
    );
}
