import { router, useForm } from '@inertiajs/react';
import { AlertTriangle, KeyRound, Pencil, Plus, RefreshCw, Send, Trash2 } from 'lucide-react';
import { useState } from 'react';
import { CopyText } from '@/components/CopyText';
import { PageHeader } from '@/components/PageHeader';
import { Pagination } from '@/components/Pagination';
import { SettingsNav } from '@/components/SettingsNav';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardBody, CardHeader } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Dialog } from '@/components/ui/dialog';
import { Input, Select } from '@/components/ui/input';
import { Field } from '@/components/ui/label';
import { Table, Td, Th } from '@/components/ui/table';
import AppLayout from '@/layouts/AppLayout';
import { dateTime } from '@/lib/format';
import type { Paginated } from '@/types';

type Endpoint = { id: number; url: string; description: string | null; events: string[]; is_active: boolean; secret_hint: string; last_success_at: string | null; last_failure_at: string | null };
type Delivery = {
    id: number; event: string; event_id: string; status: string; attempts: number; response_code: number | null; error: string | null; response_body: string | null;
    payload: unknown; endpoint: string | null; created_at: string; delivered_at: string | null; next_attempt_at: string | null; replay_of_id: number | null; replayable: boolean;
};
type Props = {
    endpoints: Endpoint[]; events: Record<string, string>; deliveries: Paginated<Delivery>; filters: { endpoint: number | null; status: string | null };
    revealed: string | null; replayDays: number; retryMinutes: number[];
};

const STATUS: Record<string, string> = { success: 'bg-teal/10 text-[#05705f]', failed: 'bg-rose/10 text-[#b3172f]', retrying: 'bg-amber/15 text-[#7a5200]', pending: 'bg-stone-100 text-stone-700' };

export default function Webhooks({ endpoints, events, deliveries, filters, revealed, replayDays, retryMinutes }: Props) {
    const [editing, setEditing] = useState<Endpoint | 'new' | null>(null);
    const [shown, setShown] = useState<Delivery | null>(null);
    const filter = (patch: Partial<Props['filters']>) => router.get('/settings/webhooks', Object.fromEntries(Object.entries({ ...filters, ...patch }).filter(([, v]) => v)), { preserveState: true, preserveScroll: true });

    return (
        <AppLayout title="Webhooks">
            <PageHeader title="Webhooks" breadcrumbs={[{ label: 'Settings', href: '/settings/company' }, { label: 'Webhooks' }]}
                description="Send this company's quote events to other systems. Each POST is signed with HMAC-SHA256 using the endpoint's secret." />
            <SettingsNav />
            <div className="grid max-w-5xl grid-cols-[minmax(0,1fr)] gap-6">
                {revealed && (
                    <Card className="border border-amber-300 bg-amber-50 p-4" data-testid="webhook-secret">
                        <p className="mb-2 flex items-center gap-2 text-sm font-medium text-amber-900"><KeyRound className="h-4 w-4" /> Signing secret: copy it now, it won’t be shown again.</p>
                        <CopyText value={revealed} />
                    </Card>
                )}
                <Card>
                    <CardHeader title="Endpoints" actions={<Button size="sm" onClick={() => setEditing('new')} data-testid="add-webhook"><Plus className="h-4 w-4" /> Add endpoint</Button>} />
                    <CardBody className="grid gap-3">
                        {endpoints.length === 0 && <p className="text-sm text-ink-muted">No endpoints yet. Add one to receive events such as quote_accepted.</p>}
                        {endpoints.map((e) => (
                            <div key={e.id} className="rounded-md border border-line p-3" data-testid="webhook-endpoint">
                                <div className="flex flex-wrap items-center gap-2">
                                    <code className="min-w-0 flex-1 break-all text-sm">{e.url}</code>
                                    {!e.is_active && <Badge className="bg-stone-100 text-stone-700">Off</Badge>}
                                    <Button size="sm" variant="secondary" onClick={() => router.post(`/settings/webhooks/${e.id}/test`, {}, { preserveScroll: true })}><Send className="h-3.5 w-3.5" /> Send test</Button>
                                    <Button size="sm" variant="ghost" onClick={() => setEditing(e)} aria-label="Edit"><Pencil className="h-3.5 w-3.5" /></Button>
                                    <Button size="sm" variant="ghost" aria-label="Delete" onClick={() => confirm('Remove this webhook endpoint?') && router.delete(`/settings/webhooks/${e.id}`, { preserveScroll: true })}><Trash2 className="h-3.5 w-3.5" /></Button>
                                </div>
                                {e.description && <p className="mt-1 text-sm text-ink-muted">{e.description}</p>}
                                <p className="mt-2 flex flex-wrap gap-1">{e.events.map((ev) => <Badge key={ev} className="bg-stone-100 font-mono text-[11px] text-stone-700">{ev}</Badge>)}</p>
                                <p className="mt-2 text-xs text-ink-muted">
                                    Secret {e.secret_hint} · <button type="button" className="text-link underline" onClick={() => confirm('Create a new signing secret? The old one stops working immediately.') && router.post(`/settings/webhooks/${e.id}/secret`, {}, { preserveScroll: true })}>Rotate</button>
                                    {e.last_success_at && <> · Last delivered {dateTime(e.last_success_at)}</>}
                                    {e.last_failure_at && <span className="text-red-700"> · Last failure {dateTime(e.last_failure_at)}</span>}
                                </p>
                            </div>
                        ))}
                    </CardBody>
                </Card>
                <Card>
                    <CardHeader title="Delivery log" description={`Failed deliveries retry ${retryMinutes.join(', ')} minutes later. Replay any delivery for ${replayDays} days.`}
                        actions={<div className="flex gap-2">
                            <Select aria-label="Endpoint" value={filters.endpoint ?? ''} onChange={(e) => filter({ endpoint: e.target.value ? Number(e.target.value) : null })} className="h-8 text-sm">
                                <option value="">All endpoints</option>{endpoints.map((e) => <option key={e.id} value={e.id}>{e.url.replace(/^https?:\/\//, '').slice(0, 40)}</option>)}
                            </Select>
                            <Select aria-label="Status" value={filters.status ?? ''} onChange={(e) => filter({ status: e.target.value || null })} className="h-8 text-sm">
                                <option value="">Any status</option><option value="success">Delivered</option><option value="retrying">Retrying</option><option value="failed">Failed</option>
                            </Select>
                        </div>} />
                    {deliveries.data.length === 0 ? <CardBody><p className="text-sm text-ink-muted">No deliveries yet.</p></CardBody> : (
                        <Table>
                            <thead><tr><Th>When</Th><Th>Event</Th><Th>Status</Th><Th>Response</Th><Th /></tr></thead>
                            <tbody>{deliveries.data.map((d) => (
                                <tr key={d.id} data-testid="webhook-delivery">
                                    <Td className="whitespace-nowrap text-sm">{dateTime(d.created_at)}{d.replay_of_id && <div className="text-xs text-ink-muted">Replay of #{d.replay_of_id}</div>}</Td>
                                    <Td><code className="text-xs">{d.event}</code><div className="max-w-[14rem] truncate text-xs text-ink-muted">{d.endpoint}</div></Td>
                                    <Td><Badge className={STATUS[d.status]}>{d.status === 'success' ? 'Delivered' : d.status[0].toUpperCase() + d.status.slice(1)}</Badge>
                                        <div className="text-xs text-ink-muted">{d.attempts} attempt{d.attempts === 1 ? '' : 's'}{d.status === 'retrying' && d.next_attempt_at ? ` · next ${dateTime(d.next_attempt_at)}` : ''}</div></Td>
                                    <Td className="text-sm">{d.response_code ?? '—'}{d.error && <div className="max-w-[11rem] truncate text-xs text-red-700" title={d.error}>{d.error}</div>}</Td>
                                    <Td className="whitespace-nowrap text-right">
                                        <Button size="sm" variant="ghost" onClick={() => setShown(d)}>Details</Button>
                                        {d.replayable && <Button size="sm" variant="secondary" onClick={() => router.post(`/settings/webhooks/deliveries/${d.id}/replay`, {}, { preserveScroll: true })} data-testid="webhook-replay"><RefreshCw className="h-3.5 w-3.5" /> Replay</Button>}
                                    </Td>
                                </tr>
                            ))}</tbody>
                        </Table>
                    )}
                    <Pagination page={deliveries} />
                </Card>
                <VerifyHelp />
            </div>
            {editing && <EndpointDialog endpoint={editing === 'new' ? null : editing} events={events} onClose={() => setEditing(null)} />}
            <Dialog open={!!shown} onOpenChange={(o) => !o && setShown(null)} title={`Delivery #${shown?.id ?? ''}`} description={shown ? `${shown.event} · event ${shown.event_id}` : ''} size="xl">
                {shown && <div className="grid gap-3 text-sm">
                    <div><div className="mb-1 font-medium">Payload</div><pre className="max-h-80 overflow-auto rounded bg-stone-100 p-3 text-xs">{JSON.stringify(shown.payload, null, 2)}</pre></div>
                    {shown.response_body && <div><div className="mb-1 font-medium">Response ({shown.response_code})</div><pre className="max-h-40 overflow-auto whitespace-pre-wrap rounded bg-stone-100 p-3 text-xs">{shown.response_body}</pre></div>}
                    {shown.error && <p className="flex gap-1.5 text-red-700"><AlertTriangle className="h-4 w-4" /> {shown.error}</p>}
                </div>}
            </Dialog>
        </AppLayout>
    );
}

function EndpointDialog({ endpoint, events, onClose }: { endpoint: Endpoint | null; events: Record<string, string>; onClose: () => void }) {
    const f = useForm({ url: endpoint?.url ?? '', description: endpoint?.description ?? '', events: endpoint?.events ?? ['quote_accepted'], is_active: endpoint?.is_active ?? true });
    const toggle = (k: string) => f.setData('events', f.data.events.includes(k) ? f.data.events.filter((e) => e !== k) : [...f.data.events, k]);
    const submit = (e: React.FormEvent) => {
        e.preventDefault();
        const opts = { preserveScroll: true, onSuccess: onClose };
        endpoint ? f.put(`/settings/webhooks/${endpoint.id}`, opts) : f.post('/settings/webhooks', opts);
    };
    return (
        <Dialog open onOpenChange={(o) => !o && onClose()} title={endpoint ? 'Edit endpoint' : 'Add endpoint'} size="lg">
            <form onSubmit={submit} className="grid gap-4">
                <Field label="URL" htmlFor="wh-url" error={f.errors.url} hint="Must be a public https:// address."><Input id="wh-url" value={f.data.url} onChange={(e) => f.setData('url', e.target.value)} placeholder="https://example.com/webhooks/spin" /></Field>
                <Field label="Description" htmlFor="wh-desc"><Input id="wh-desc" value={f.data.description} onChange={(e) => f.setData('description', e.target.value)} placeholder="e.g. Zapier → Slack" /></Field>
                <fieldset>
                    <legend className="mb-1 text-sm font-medium">Events</legend>
                    <div className="grid gap-1.5 sm:grid-cols-2">
                        {Object.entries(events).map(([k, label]) => <Checkbox key={k} label={<span><code className="text-xs">{k}</code> <span className="text-ink-muted">{label}</span></span>} checked={f.data.events.includes(k)} onChange={() => toggle(k)} />)}
                    </div>
                    {(f.errors.events || (f.errors as Record<string, string>)['events.0']) && <p className="mt-1 text-sm text-danger">{f.errors.events ?? (f.errors as Record<string, string>)['events.0']}</p>}
                </fieldset>
                <Checkbox label="Active" checked={f.data.is_active} onChange={(e) => f.setData('is_active', e.target.checked)} />
                <div className="flex justify-end gap-2"><Button type="button" variant="secondary" onClick={onClose}>Cancel</Button><Button disabled={f.processing} data-testid="save-webhook">Save</Button></div>
            </form>
        </Dialog>
    );
}

function VerifyHelp() {
    const code = `// Node.js: verify X-Spin-Signature (t=<unix>,v1=<hex>)
const [, t, v1] = header.match(/t=(\\d+),v1=([a-f0-9]{64})/);
const expected = crypto.createHmac('sha256', secret).update(\`\${t}.\${rawBody}\`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) < 300;`;
    return (
        <Card>
            <CardHeader title="Verifying signatures" />
            <CardBody className="grid gap-2 text-sm">
                <p>Every request has <code>X-Spin-Event</code>, <code>X-Spin-Delivery</code> (the event id, the same on replays) and <code>X-Spin-Signature</code>. Compute HMAC-SHA256 of <code>{'{t}.{raw body}'}</code> with the secret and compare with <code>v1</code>. Reply with any 2xx within 15 seconds.</p>
                <pre className="overflow-auto rounded bg-stone-100 p-3 text-xs">{code}</pre>
            </CardBody>
        </Card>
    );
}
