import React, { useEffect, useRef, useState } from 'react';
import type { Locale, LangCode } from './i18n';
import { jsonHeaders } from './csrf';

export interface NewsCategory {
    id: number;
    slug: string;
    name: Record<LangCode, string>;
}

type Errors = Partial<Record<'name' | 'email' | 'topics' | 'code' | 'form', string>>;

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

/** Matches SubscriberController::RESEND_COOLDOWN_SECONDS. */
const RESEND_WAIT_SECONDS = 60;

async function sendJson(url: string, body: unknown, method = 'POST'): Promise<Response> {
    return fetch(url, { method, headers: jsonHeaders(), body: JSON.stringify(body) });
}

/**
 * A native <dialog> opened as a modal. Padding lives on the inner block, so a
 * click that lands on the <dialog> element itself is a click on the backdrop.
 */
function Modal({ open, onClose, label, closeLabel, children }: {
    open: boolean;
    onClose: () => void;
    label: string;
    closeLabel: string;
    children: React.ReactNode;
}) {
    const ref = useRef<HTMLDialogElement | null>(null);
    // The native `close` event fires a tick after dialog.close(). When we closed it
    // ourselves (e.g. switching to another dialog) the parent already knows, and
    // passing the event on would shut whichever dialog was opened in its place.
    const openRef = useRef(open);

    useEffect(() => {
        openRef.current = open;
        const dialog = ref.current;
        if (!dialog) return;
        if (open && !dialog.open) dialog.showModal();
        if (!open && dialog.open) dialog.close();
    }, [open]);

    return (
        <dialog
            ref={ref}
            className="subscribe-dialog"
            aria-label={label}
            onClose={() => {
                if (openRef.current) onClose();
            }}
            onClick={(e) => {
                if (e.target === ref.current) onClose();
            }}
        >
            <button className="subscribe-dialog-x" onClick={onClose} aria-label={closeLabel}>
                <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
                    <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" />
                </svg>
            </button>
            {children}
        </dialog>
    );
}

/** "All news" or a chosen set of categories. */
function TopicPicker({ t, lang, categories, all, chosen, onChange, error }: {
    t: Locale;
    lang: LangCode;
    categories: NewsCategory[];
    all: boolean;
    chosen: number[];
    onChange: (all: boolean, chosen: number[]) => void;
    error?: string;
}) {
    if (categories.length === 0) return null;
    const toggle = (id: number) =>
        onChange(false, chosen.includes(id) ? chosen.filter((c) => c !== id) : [...chosen, id]);

    return (
        <fieldset className="sf-topics">
            <legend>{t.subscribe.topics}</legend>
            <label className={'sf-chip' + (all ? ' on' : '')}>
                <input type="checkbox" checked={all} onChange={(e) => onChange(e.target.checked, e.target.checked ? [] : chosen)} />
                {t.subscribe.allTopics}
            </label>
            {categories.map((c) => (
                <label key={c.id} className={'sf-chip' + (!all && chosen.includes(c.id) ? ' on' : '')}>
                    <input type="checkbox" checked={!all && chosen.includes(c.id)} onChange={() => toggle(c.id)} />
                    {c.name[lang]}
                </label>
            ))}
            {error && <em>{error}</em>}
        </fieldset>
    );
}

function Honeypot({ value, onChange }: { value: string; onChange: (v: string) => void }) {
    return (
        <input
            className="subscribe-hp"
            type="text"
            name="website"
            tabIndex={-1}
            autoComplete="off"
            aria-hidden="true"
            value={value}
            onChange={(e) => onChange(e.target.value)}
        />
    );
}

/** The band above the page footer: a short pitch, Subscribe, and Manage subscription. */
export function SubscribeBand({ t, lang, categories }: { t: Locale; lang: LangCode; categories: NewsCategory[] }) {
    const s = t.subscribe;
    const [dialog, setDialog] = useState<'subscribe' | 'manage' | null>(null);
    return (
        <div className="subscribe">
            <div className="subscribe-copy">
                <span className="kicker">{s.kicker}</span>
                <h2>{s.title}</h2>
                <p>{s.sub}</p>
            </div>
            <div className="subscribe-cta">
                <button className="btn btn-primary" onClick={() => setDialog('subscribe')}>
                    {s.open} <span className="arrow">→</span>
                </button>
                <p className="subscribe-note">
                    {s.privacy}{' '}
                    <button type="button" className="link-btn" onClick={() => setDialog('manage')}>
                        {s.manageLink}
                    </button>
                </p>
            </div>
            <SubscribeDialog
                t={t}
                lang={lang}
                categories={categories}
                open={dialog === 'subscribe'}
                onClose={() => setDialog(null)}
                onManage={() => setDialog('manage')}
            />
            <ManageDialog t={t} lang={lang} categories={categories} open={dialog === 'manage'} onClose={() => setDialog(null)} />
        </div>
    );
}

function SubscribeDialog({ t, lang, categories, open, onClose, onManage }: {
    t: Locale;
    lang: LangCode;
    categories: NewsCategory[];
    open: boolean;
    onClose: () => void;
    onManage: () => void;
}) {
    const s = t.subscribe;
    const [name, setName] = useState('');
    const [organization, setOrganization] = useState('');
    const [email, setEmail] = useState('');
    const [all, setAll] = useState(true);
    const [chosen, setChosen] = useState<number[]>([]);
    const [website, setWebsite] = useState('');
    const [busy, setBusy] = useState(false);
    const [done, setDone] = useState(false);
    const [errors, setErrors] = useState<Errors>({});
    // The server sends at most one confirmation a minute; the resend button waits it out.
    const [resendIn, setResendIn] = useState(0);
    const [resent, setResent] = useState(false);

    useEffect(() => {
        if (resendIn <= 0) return;
        const timer = setTimeout(() => setResendIn((n) => n - 1), 1000);
        return () => clearTimeout(timer);
    }, [resendIn]);

    const payload = () => ({
        name: name.trim(),
        organization: organization.trim() || null,
        email: email.trim(),
        all_categories: all,
        categories: all ? [] : chosen,
        lang,
        website,
    });

    const resend = async () => {
        if (busy || resendIn > 0) return;
        setBusy(true);
        setErrors({});
        try {
            const res = await sendJson('/subscribe', payload());
            if (res.ok) {
                setResent(true);
                setResendIn(RESEND_WAIT_SECONDS);
            } else {
                setErrors({ form: res.status === 429 ? t.manage.tooMany : s.error });
            }
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const close = () => {
        onClose();
        if (done) {
            setDone(false);
            setResent(false);
            setName('');
            setOrganization('');
            setEmail('');
            setAll(true);
            setChosen([]);
        }
        setErrors({});
    };

    const onSubmit = async (ev: React.FormEvent) => {
        ev.preventDefault();
        if (busy) return;
        const e: Errors = {};
        if (!name.trim()) e.name = s.nameRequired;
        if (!EMAIL_RE.test(email.trim())) e.email = s.invalidEmail;
        if (!all && chosen.length === 0) e.topics = s.topicRequired;
        setErrors(e);
        if (Object.keys(e).length) return;

        setBusy(true);
        try {
            const res = await sendJson('/subscribe', payload());
            if (res.ok) {
                setDone(true);
                setResent(false);
                setResendIn(RESEND_WAIT_SECONDS);
            } else if (res.status === 422) {
                const fields = (await res.json().catch(() => ({}))).errors ?? {};
                setErrors({
                    name: fields.name ? s.nameRequired : undefined,
                    email: fields.email ? s.invalidEmail : undefined,
                    topics: fields.categories ? s.topicRequired : undefined,
                    form: Object.keys(fields).some((k) => !['name', 'email', 'categories'].includes(k)) ? s.error : undefined,
                });
            } else {
                setErrors({ form: s.error });
            }
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    return (
        <Modal open={open} onClose={close} label={s.dialogTitle} closeLabel={s.close}>
            {done ? (
                <div className="subscribe-done">
                    <img src="/assets/owl.png" alt="" className="subscription-owl" draggable={false} />
                    <h2>{s.successTitle}</h2>
                    <p>
                        {s.successBody} <strong>{email.trim()}</strong>
                    </p>
                    <button className="btn btn-primary" onClick={close}>
                        {s.done}
                    </button>
                    <p className="sf-consent">
                        {resent ? s.resent + ' ' : ''}
                        {s.noEmail}{' '}
                        <button type="button" className="link-btn" onClick={resend} disabled={busy || resendIn > 0}>
                            {resendIn > 0 ? s.resendIn.replace(':s', String(resendIn)) : s.resend}
                        </button>
                    </p>
                    {errors.form && <p className="sf-error">{errors.form}</p>}
                </div>
            ) : (
                <form className="subscribe-form" onSubmit={onSubmit} noValidate>
                    <span className="kicker">{s.kicker}</span>
                    <h2>{s.dialogTitle}</h2>
                    <p className="subscribe-dialog-sub">{s.dialogSub}</p>

                    <div className="sf-grid">
                        <label className="sf-field">
                            <span>{s.name}</span>
                            <input value={name} onChange={(e) => setName(e.target.value)} autoComplete="name" aria-invalid={!!errors.name} />
                            {errors.name && <em>{errors.name}</em>}
                        </label>
                        <label className="sf-field">
                            <span>
                                {s.organization} <small>({s.optional})</small>
                            </span>
                            <input value={organization} onChange={(e) => setOrganization(e.target.value)} autoComplete="organization" />
                        </label>
                        <label className="sf-field sf-wide">
                            <span>{s.email}</span>
                            <input
                                type="email"
                                value={email}
                                onChange={(e) => setEmail(e.target.value)}
                                autoComplete="email"
                                placeholder="you@example.com"
                                aria-invalid={!!errors.email}
                            />
                            {errors.email && <em>{errors.email}</em>}
                        </label>
                    </div>

                    <TopicPicker
                        t={t}
                        lang={lang}
                        categories={categories}
                        all={all}
                        chosen={chosen}
                        onChange={(a, c) => {
                            setAll(a);
                            setChosen(c);
                        }}
                        error={errors.topics}
                    />

                    <Honeypot value={website} onChange={setWebsite} />
                    {errors.form && <p className="sf-error">{errors.form}</p>}
                    <div className="sf-actions">
                        <button className="btn btn-primary" type="submit" disabled={busy}>
                            {busy ? s.sending : s.button}
                        </button>
                    </div>
                    <p className="sf-consent">{s.consent}</p>
                    <p className="sf-consent">
                        {s.alreadySubscribed}{' '}
                        <button type="button" className="link-btn" onClick={onManage}>
                            {s.manageLink}
                        </button>
                    </p>
                </form>
            )}
        </Modal>
    );
}

interface Subscription {
    email: string;
    name: string | null;
    organization: string | null;
    lang: LangCode;
    status: 'active' | 'pending' | 'unsubscribed';
    subscribed_at: string | null;
    just_confirmed?: boolean;
    all_categories: boolean;
    categories: number[];
}

type ManageStep = 'email' | 'code' | 'account' | 'left';

/**
 * Log in with a one-time code, then view and edit the subscription. Nothing
 * about it is shown or changed until the code from the inbox has been entered.
 */
function ManageDialog({ t, lang, categories, open, onClose }: {
    t: Locale;
    lang: LangCode;
    categories: NewsCategory[];
    open: boolean;
    onClose: () => void;
}) {
    const m = t.manage;
    const s = t.subscribe;
    const [step, setStep] = useState<ManageStep>('email');
    const [email, setEmail] = useState('');
    const [code, setCode] = useState('');
    const [account, setAccount] = useState<Subscription | null>(null);
    const [name, setName] = useState('');
    const [organization, setOrganization] = useState('');
    const [mailLang, setMailLang] = useState<LangCode>(lang);
    const [all, setAll] = useState(true);
    const [chosen, setChosen] = useState<number[]>([]);
    // Cancelling is a second layer: a fresh code is emailed and must be entered.
    const [leaving, setLeaving] = useState<null | 'ask' | 'code'>(null);
    const [leaveReason, setLeaveReason] = useState<'button' | 'empty'>('button');
    const [leaveCode, setLeaveCode] = useState('');
    const [busy, setBusy] = useState(false);
    const [notice, setNotice] = useState('');
    const [errors, setErrors] = useState<Errors>({});

    const reset = () => {
        setStep('email');
        setCode('');
        setAccount(null);
        setLeaving(null);
        setLeaveCode('');
        setNotice('');
        setErrors({});
    };

    const close = () => {
        onClose();
        reset();
    };

    const load = (a: Subscription) => {
        setAccount(a);
        setName(a.name ?? '');
        setOrganization(a.organization ?? '');
        setMailLang(a.lang);
        setAll(a.all_categories);
        setChosen(a.categories);
    };

    const failed = (res: Response) => {
        if (res.status === 429) return setErrors({ form: m.tooMany });
        if (res.status === 403 || res.status === 419) {
            reset();
            return setErrors({ form: m.expired });
        }
        setErrors({ form: s.error });
    };

    const sendCode = async (ev?: React.FormEvent) => {
        ev?.preventDefault();
        if (busy) return;
        if (!EMAIL_RE.test(email.trim())) return setErrors({ email: s.invalidEmail });
        setErrors({});
        setBusy(true);
        try {
            const res = await sendJson('/subscription/code', { email: email.trim(), lang });
            if (res.ok) {
                setStep('code');
                setCode('');
            } else failed(res);
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const verify = async (ev: React.FormEvent) => {
        ev.preventDefault();
        if (busy) return;
        if (!/^\d{6}$/.test(code.trim())) return setErrors({ code: m.wrongCode });
        setErrors({});
        setBusy(true);
        try {
            const res = await sendJson('/subscription/verify', { email: email.trim(), code: code.trim() });
            if (res.ok) {
                const a = (await res.json()) as Subscription;
                load(a);
                setNotice(a.just_confirmed ? m.pendingNote : '');
                setStep('account');
            } else if (res.status === 422) {
                setErrors({ code: m.wrongCode });
            } else failed(res);
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const save = async () => {
        if (busy) return;
        // No topics at all means no news at all: treat it as cancelling.
        if (!all && chosen.length === 0 && account?.status !== 'unsubscribed') {
            setLeaveReason('empty');
            setLeaving('ask');
            return;
        }
        const e: Errors = {};
        if (!name.trim()) e.name = s.nameRequired;
        if (!all && chosen.length === 0) e.topics = s.topicRequired;
        setErrors(e);
        setNotice('');
        if (Object.keys(e).length) return;
        setBusy(true);
        try {
            const res = await sendJson(
                '/subscription',
                {
                    name: name.trim(),
                    organization: organization.trim() || null,
                    lang: mailLang,
                    all_categories: all,
                    categories: all ? [] : chosen,
                },
                'PUT',
            );
            if (res.ok) {
                load((await res.json()) as Subscription);
                setNotice(m.saved);
            } else failed(res);
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const sendLeaveCode = async () => {
        if (busy) return;
        setBusy(true);
        setErrors({});
        try {
            const res = await sendJson('/subscription/unsubscribe/code', {});
            if (res.ok) {
                setLeaving('code');
                setLeaveCode('');
            } else failed(res);
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const leave = async (ev: React.FormEvent) => {
        ev.preventDefault();
        if (busy) return;
        if (!/^\d{6}$/.test(leaveCode.trim())) return setErrors({ code: m.wrongCode });
        setBusy(true);
        setErrors({});
        try {
            const res = await sendJson('/subscription/unsubscribe', { code: leaveCode.trim() });
            if (res.ok) {
                load((await res.json()) as Subscription);
                setStep('left');
            } else if (res.status === 422) {
                setErrors({ code: m.wrongCode });
            } else failed(res);
        } catch {
            setErrors({ form: s.error });
        } finally {
            setBusy(false);
        }
    };

    const since = account?.subscribed_at
        ? new Date(account.subscribed_at).toLocaleDateString(lang === 'zh' ? 'zh-HK' : 'en-GB', { year: 'numeric', month: 'short', day: 'numeric' })
        : null;

    return (
        <Modal open={open} onClose={close} label={m.title} closeLabel={s.close}>
            <div className="subscribe-form">
                <span className="kicker">{s.kicker}</span>
                <h2>{m.title}</h2>

                {step === 'email' && (
                    <form onSubmit={sendCode} noValidate>
                        <p className="subscribe-dialog-sub">{m.emailStep}</p>
                        <label className="sf-field sf-block">
                            <span>{s.email}</span>
                            <input
                                type="email"
                                value={email}
                                onChange={(e) => setEmail(e.target.value)}
                                autoComplete="email"
                                placeholder="you@example.com"
                                aria-invalid={!!errors.email}
                            />
                            {errors.email && <em>{errors.email}</em>}
                        </label>
                        {errors.form && <p className="sf-error">{errors.form}</p>}
                        <div className="sf-actions">
                            <button className="btn btn-primary" type="submit" disabled={busy}>
                                {busy ? m.sendingCode : m.sendCode}
                            </button>
                        </div>
                    </form>
                )}

                {step === 'code' && (
                    <form onSubmit={verify} noValidate>
                        <p className="subscribe-dialog-sub">
                            {m.codeStep} <strong>{email}</strong>
                        </p>
                        <label className="sf-field sf-block">
                            <span>{m.code}</span>
                            <input
                                className="sf-code"
                                inputMode="numeric"
                                autoComplete="one-time-code"
                                maxLength={6}
                                value={code}
                                onChange={(e) => setCode(e.target.value.replace(/\D/g, ''))}
                                placeholder="••••••"
                                aria-invalid={!!errors.code}
                                autoFocus
                            />
                            {errors.code && <em>{errors.code}</em>}
                        </label>
                        {errors.form && <p className="sf-error">{errors.form}</p>}
                        <div className="sf-actions">
                            <button className="btn btn-primary" type="submit" disabled={busy}>
                                {busy ? m.verifying : m.verify}
                            </button>
                        </div>
                        <p className="sf-consent">
                            <button type="button" className="link-btn" onClick={() => sendCode()} disabled={busy}>
                                {m.resend}
                            </button>
                            {' · '}
                            <button type="button" className="link-btn" onClick={reset}>
                                {m.otherEmail}
                            </button>
                        </p>
                    </form>
                )}

                {step === 'account' && account && (
                    <div>
                        <dl className="sf-account">
                            <div>
                                <dt>{s.email}</dt>
                                <dd>{account.email}</dd>
                            </div>
                            <div>
                                <dt>{m.status}</dt>
                                <dd>
                                    <span className={'sf-status ' + account.status}>{m.statuses[account.status]}</span>
                                    {since && account.status === 'active' && (
                                        <small>
                                            {' '}
                                            · {m.since} {since}
                                        </small>
                                    )}
                                </dd>
                            </div>
                        </dl>
                        {account.status === 'unsubscribed' && <p className="sf-hint">{m.unsubscribedNote}</p>}

                        <div className="sf-grid">
                            <label className="sf-field">
                                <span>{s.name}</span>
                                <input value={name} onChange={(e) => setName(e.target.value)} autoComplete="name" aria-invalid={!!errors.name} />
                                {errors.name && <em>{errors.name}</em>}
                            </label>
                            <label className="sf-field">
                                <span>
                                    {s.organization} <small>({s.optional})</small>
                                </span>
                                <input value={organization} onChange={(e) => setOrganization(e.target.value)} autoComplete="organization" />
                            </label>
                        </div>

                        <fieldset className="sf-topics">
                            <legend>{m.emailLanguage}</legend>
                            {(['en', 'zh'] as const).map((l) => (
                                <label key={l} className={'sf-chip' + (mailLang === l ? ' on' : '')}>
                                    <input type="radio" name="mail-lang" checked={mailLang === l} onChange={() => setMailLang(l)} />
                                    {l === 'en' ? 'English' : '中文'}
                                </label>
                            ))}
                        </fieldset>

                        <TopicPicker
                            t={t}
                            lang={lang}
                            categories={categories}
                            all={all}
                            chosen={chosen}
                            onChange={(a, c) => {
                                setAll(a);
                                setChosen(c);
                                setNotice('');
                            }}
                            error={errors.topics}
                        />
                        {errors.form && <p className="sf-error">{errors.form}</p>}
                        {notice && <p className="sf-ok">{notice}</p>}
                        <div className="sf-actions">
                            <button className="btn btn-primary" onClick={save} disabled={busy}>
                                {busy ? m.saving : account.status === 'unsubscribed' ? m.resubscribe : m.save}
                            </button>
                        </div>
                        {account.status !== 'unsubscribed' && (
                            <div className="sf-danger">
                                {leaving === null && (
                                    <button
                                        type="button"
                                        className="link-btn danger"
                                        onClick={() => {
                                            setLeaveReason('button');
                                            setLeaving('ask');
                                        }}
                                    >
                                        {m.unsubscribe}
                                    </button>
                                )}
                                {leaving === 'ask' && (
                                    <>
                                        <p>{leaveReason === 'empty' ? m.emptyMeansCancel : m.unsubscribeAsk}</p>
                                        <button className="btn btn-danger" onClick={sendLeaveCode} disabled={busy}>
                                            {busy ? m.sendingCode : m.sendCancelCode}
                                        </button>{' '}
                                        <button type="button" className="link-btn" onClick={() => setLeaving(null)}>
                                            {m.keep}
                                        </button>
                                    </>
                                )}
                                {leaving === 'code' && (
                                    <form onSubmit={leave} noValidate>
                                        <p>
                                            {m.cancelCodeSent} <strong>{account.email}</strong>
                                        </p>
                                        <label className="sf-field sf-block">
                                            <span>{m.code}</span>
                                            <input
                                                className="sf-code"
                                                inputMode="numeric"
                                                autoComplete="one-time-code"
                                                maxLength={6}
                                                value={leaveCode}
                                                onChange={(e) => setLeaveCode(e.target.value.replace(/\D/g, ''))}
                                                placeholder="••••••"
                                                aria-invalid={!!errors.code}
                                                autoFocus
                                            />
                                            {errors.code && <em>{errors.code}</em>}
                                        </label>
                                        <button className="btn btn-danger" type="submit" disabled={busy}>
                                            {m.unsubscribeConfirm}
                                        </button>{' '}
                                        <button type="button" className="link-btn" onClick={() => setLeaving(null)}>
                                            {m.keep}
                                        </button>
                                    </form>
                                )}
                            </div>
                        )}
                    </div>
                )}

                {step === 'left' && (
                    <div className="subscribe-done">
                        <p>{m.unsubscribed}</p>
                        <button className="btn btn-primary" onClick={close}>
                            {s.done}
                        </button>
                    </div>
                )}
            </div>
        </Modal>
    );
}

interface SubscriptionProps {
    status: 'confirmed' | 'unsubscribe' | 'unsubscribed' | 'invalid';
    token?: string;
    email?: string;
}

/** Landing pages for the links in our emails: confirm, and one-click unsubscribe. */
export function SubscriptionPage({ t, props }: { t: Locale; props: SubscriptionProps }) {
    const c = t.subscription;
    const [status, setStatus] = useState(props.status);
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState('');

    const unsubscribe = async () => {
        setBusy(true);
        setError('');
        try {
            const res = await sendJson('/unsubscribe/' + encodeURIComponent(props.token ?? ''), {});
            if (!res.ok) throw new Error('unsubscribe failed');
            setStatus('unsubscribed');
        } catch {
            setError(c.error);
        } finally {
            setBusy(false);
        }
    };

    const copy = {
        confirmed: [c.confirmedTitle, c.confirmedBody],
        unsubscribe: [c.unsubscribeTitle, c.unsubscribeBody],
        unsubscribed: [c.unsubscribedTitle, c.unsubscribedBody],
        invalid: [c.invalidTitle, c.invalidBody],
    }[status];

    return (
        <section className="news-page">
            <div className="wrap subscription-card">
                <img src="/assets/owl.png" alt="" className="subscription-owl" draggable={false} />
                <h1>{copy[0]}</h1>
                <p>
                    {copy[1]}
                    {status === 'unsubscribe' && props.email && (
                        <>
                            {' '}
                            <strong>{props.email}</strong>
                        </>
                    )}
                </p>
                {error && <p className="sf-error">{error}</p>}
                <div className="subscription-actions">
                    {status === 'unsubscribe' && (
                        <button className="btn btn-primary" onClick={unsubscribe} disabled={busy}>
                            {c.unsubscribeButton}
                        </button>
                    )}
                    <a className="btn btn-ghost" href="/news">
                        {c.toNews} <span className="arrow">→</span>
                    </a>
                </div>
            </div>
        </section>
    );
}
