import { Collapsible } from '@base-ui/react/collapsible';
import { RewardTrackAdminPrize, RewardTrackAdminTask, RewardTrackAdminTrack } from '@octane/renderer';
import { FC, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import { localizeWithFallback, reorderRewardTrackEntries, RewardTrackFieldErrors, validateRewardTrackInput, validateRewardTrackPrizeInput, validateRewardTrackTaskInput } from '../../api';
import { Button, LayoutFurniIconImageView } from '../../common';
import { RewardTrackAdminEntity, RewardTrackAdminPrizeInput, RewardTrackAdminTaskInput, RewardTrackAdminTrackInput, useRewardTrackAdmin } from '../../hooks';
import { RewardTrackAdminModalView } from './RewardTrackAdminModalView';
import { Checkbox } from '../../common/Checkbox';
import { Select } from '../../common/Select';
import { NumberInput } from '../../common/NumberInput';
import { BaseButton } from '../../common/BaseButton';
import { Hint } from '../../common/Tooltip';
import { NativeTextTree } from '../../common/native-text/NativeTextTree';

const text = (key: string, fallback: string) => localizeWithFallback(`reward_track.admin.${key}`, fallback);

/** A unix timestamp (seconds, 0 for none) as the value of a datetime-local input, in local time. */
export const unixToLocalInput = (seconds: number): string => {
    if (!seconds || seconds <= 0) return '';

    const date = new Date(seconds * 1000);
    const pad = (value: number) => String(value).padStart(2, '0');

    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
};

/** The value of a datetime-local input back to unix seconds, 0 when empty or unreadable. */
export const localInputToUnix = (value: string): number => {
    if (!value) return 0;

    const millis = new Date(value).getTime();

    return Number.isFinite(millis) && millis > 0 ? Math.floor(millis / 1000) : 0;
};

export type RewardTrackAdminFilter = 'all' | 'active' | 'scheduled' | 'expired' | 'off';

/** Where a track stands now: off, not started yet, over, or running. */
export const getRewardTrackAdminState = (track: { enabled: boolean; startsAt: number; endsAt: number }, now = Math.floor(Date.now() / 1000)): Exclude<RewardTrackAdminFilter, 'all'> => {
    if (!track.enabled) return 'off';
    if (track.startsAt > 0 && track.startsAt > now) return 'scheduled';
    if (track.endsAt > 0 && track.endsAt <= now) return 'expired';

    return 'active';
};

const FILTERS: RewardTrackAdminFilter[] = ['all', 'active', 'scheduled', 'expired', 'off'];

/** The text keys of a track and its tasks, in the order the editor shows them. */
export const getRewardTrackTextKeys = (taskIds: string[]): { key: string; label: string }[] => [
    { key: 'name', label: text('texts.name', 'Track name') },
    { key: 'desc', label: text('texts.desc', 'Track description') },
    { key: 'info', label: text('texts.info', 'Track info line') },
    ...taskIds.flatMap((taskId) => [
        { key: `task.${taskId}.name`, label: `${taskId} · ${text('texts.task.name', 'name')}` },
        { key: `task.${taskId}.desc`, label: `${taskId} · ${text('texts.task.desc', 'description')}` },
        { key: `task.${taskId}.hint.desc`, label: `${taskId} · ${text('texts.task.hint', 'hint')}` },
        { key: `task.${taskId}.hint.button_text`, label: `${taskId} · ${text('texts.task.button', 'hint button')}` }
    ])
];

const formatDate = (seconds: number): string => (seconds > 0 ? new Date(seconds * 1000).toLocaleDateString() : '');

const toInt = (value: string, fallback = 0): number => {
    const parsed = Number.parseInt(value, 10);

    return Number.isFinite(parsed) ? parsed : fallback;
};

const emptyTrack = (): RewardTrackAdminTrackInput => ({
    id: '',
    theme: 'blue',
    sortOrder: 0,
    startsAt: 0,
    endsAt: 0,
    hasPremium: false,
    premiumBoostPercent: 100,
    premiumInstantPoints: 0,
    premiumCostDiamonds: 0,
    premiumCostCredits: 0,
    enabled: true
});

const trackToInput = (track: RewardTrackAdminTrack): RewardTrackAdminTrackInput => ({
    id: track.id,
    theme: track.theme,
    sortOrder: track.sortOrder,
    startsAt: track.startsAt,
    endsAt: track.endsAt,
    hasPremium: track.hasPremium,
    premiumBoostPercent: track.premiumBoostPercent,
    premiumInstantPoints: track.premiumInstantPoints,
    premiumCostDiamonds: track.premiumCostDiamonds,
    premiumCostCredits: track.premiumCostCredits,
    enabled: track.enabled
});

const emptyTask = (trackId: string, actionType: string, sortOrder: number): RewardTrackAdminTaskInput => ({
    trackId,
    id: '',
    actionType,
    parameter: '',
    premium: false,
    sortOrder,
    levels: [{ requiredCount: 1, pointsReward: 10, premium: false }]
});

const taskToInput = (trackId: string, task: RewardTrackAdminTask): RewardTrackAdminTaskInput => ({
    trackId,
    id: task.id,
    actionType: task.actionType,
    parameter: task.parameter,
    premium: task.premium,
    sortOrder: task.sortOrder,
    levels: task.levels.map((level) => ({ ...level }))
});

const emptyPrize = (trackId: string, rewardType: string, sortOrder: number): RewardTrackAdminPrizeInput => ({
    trackId,
    id: '',
    requiredPoints: 0,
    productItemTypeId: 0,
    rewardType,
    extraParams: '',
    rewardAmount: 1,
    premium: false,
    sortOrder
});

const prizeToInput = (trackId: string, prize: RewardTrackAdminPrize): RewardTrackAdminPrizeInput => ({
    trackId,
    id: prize.id,
    requiredPoints: prize.requiredPoints,
    productItemTypeId: prize.productItemTypeId,
    rewardType: prize.rewardType,
    extraParams: prize.extraParams,
    rewardAmount: prize.rewardAmount,
    premium: prize.premium,
    sortOrder: prize.sortOrder
});

/** The twin of a prize on the other row: the free one gets a premium copy 20 points further, and the other way round. */
const prizeTwin = (trackId: string, prize: RewardTrackAdminPrize): RewardTrackAdminPrizeInput => ({
    ...prizeToInput(trackId, prize),
    id: prize.premium ? prize.id.replace(/_premium$/, '') + '_free' : prize.id + '_premium',
    premium: !prize.premium,
    requiredPoints: prize.premium ? Math.max(0, prize.requiredPoints - 20) : prize.requiredPoints + 20,
    sortOrder: prize.sortOrder + 1
});

const BTN = ['octane-reward-track-admin-btn'];

/** A delete button that asks once: the first click arms it, the second one deletes. */
const DeleteButton: FC<{ armed: boolean; disabled?: boolean; onArm: () => void; onConfirm: () => void }> = ({ armed, disabled = false, onArm, onConfirm }) => (
    <NativeTextTree>
        <Button variant={armed ? 'danger' : 'secondary'} classNames={BTN} disabled={disabled} onClick={armed ? onConfirm : onArm} data-testid={armed ? 'rt-admin-delete-confirm' : 'rt-admin-delete'}>
            {armed ? text('delete.confirm', 'Confirm') : text('delete', 'Delete')}
        </Button>
    </NativeTextTree>
);

const Field: FC<{ label: string; error?: string; children: ReactNode; span?: number }> = ({ label, error = null, children, span = 1 }) => (
    <NativeTextTree>
        <label className={`octane-reward-track-admin-field${error ? ' has-error' : ''}`} style={span > 1 ? { gridColumn: `span ${span}` } : undefined}>
            <span className="octane-reward-track-admin-label">{label}</span>
            {children}
            {error && <span className="octane-reward-track-admin-field-error">{error}</span>}
        </label>
    </NativeTextTree>
);

const Toggle: FC<{ label: string; checked: boolean; onChange: (checked: boolean) => void }> = ({ label, checked, onChange }) => (
    <NativeTextTree>
        <label className="octane-reward-track-admin-toggle-field">
            <Checkbox checked={checked} onCheckedChange={(nextChecked) => onChange(nextChecked)} />
            <span>{label}</span>
        </label>
    </NativeTextTree>
);

const Tag: FC<{ kind?: 'premium' | 'off' | 'on' | 'info' | 'scheduled' | 'expired'; children: ReactNode }> = ({ kind = 'info', children }) => <span className={`octane-reward-track-admin-tag is-${kind}`}>{children}</span>;

const StateTag: FC<{ state: Exclude<RewardTrackAdminFilter, 'all'> }> = ({ state }) => {
    switch (state) {
        case 'off':
            return <Tag kind="off">{text('state.off', 'off')}</Tag>;
        case 'scheduled':
            return <Tag kind="scheduled">{text('state.scheduled', 'scheduled')}</Tag>;
        case 'expired':
            return <Tag kind="expired">{text('state.expired', 'expired')}</Tag>;
        default:
            return <Tag kind="on">{text('state.active', 'active')}</Tag>;
    }
};

/** A bordered panel; with a collapseKey its head folds it, and it starts folded every time the editor opens. */
const Panel: FC<{ title: ReactNode; summary?: ReactNode; actions?: ReactNode; children: ReactNode; className?: string; collapseKey?: string }> = ({
    title,
    summary = null,
    actions = null,
    children,
    className = '',
    collapseKey = null
}) => {
    const [collapsed, setCollapsed] = useState(!!collapseKey);

    return (
        <NativeTextTree>
            <Collapsible.Root
                render={<section />}
                className={`octane-reward-track-admin-panel ${className}${collapsed ? ' is-collapsed' : ''}`.trim()}
                open={!collapsed}
                onOpenChange={(open) => setCollapsed(!open)}
            >
                <header
                    className={`octane-reward-track-admin-panel-head${collapseKey ? ' is-toggle' : ''}`}
                    // The whole head folds the panel; the chevron is the Collapsible trigger, so its own click is not counted twice.
                    onClick={collapseKey ? (event) => !(event.target as HTMLElement).closest('.octane-reward-track-admin-chevron') && setCollapsed((value) => !value) : undefined}
                >
                    {collapseKey && (
                        <Collapsible.Trigger className="octane-reward-track-admin-chevron" data-testid={`rt-admin-collapse-${collapseKey}`}>
                            {collapsed ? '▸' : '▾'}
                        </Collapsible.Trigger>
                    )}
                    <span className="octane-reward-track-admin-panel-title">{title}</span>
                    {collapsed && summary && <span className="octane-reward-track-admin-panel-summary">{summary}</span>}
                    {actions && (
                        <span className="octane-reward-track-admin-panel-actions" onClick={(event) => event.stopPropagation()}>
                            {actions}
                        </span>
                    )}
                </header>
                <Collapsible.Panel className="contents">{children}</Collapsible.Panel>
            </Collapsible.Root>
        </NativeTextTree>
    );
};

const SaveCancel: FC<{ disabled: boolean; errorCount: number; onSave: () => void; onCancel: () => void; before?: ReactNode }> = ({ disabled, errorCount, onSave, onCancel, before = null }) => (
    <NativeTextTree>
        <div className="octane-reward-track-admin-actions">
            {before}
            <span className="octane-reward-track-admin-spacer" />
            {errorCount > 0 && (
                <span className="octane-reward-track-admin-form-problems" data-testid="rt-admin-form-problems">
                    {errorCount === 1 ? text('form.problem', '1 field to fix') : text('form.problems', '%count% fields to fix').replace('%count%', String(errorCount))}
                </span>
            )}
            <Button variant="success" classNames={BTN} disabled={disabled || errorCount > 0} onClick={() => !disabled && errorCount === 0 && onSave()}>
                {text('save', 'Save')}
            </Button>
            <Button variant="secondary" classNames={BTN} onClick={onCancel}>
                {text('cancel', 'Cancel')}
            </Button>
        </div>
    </NativeTextTree>
);

const hasErrors = (errors: RewardTrackFieldErrors) => Object.keys(errors).length;

const FURNI_SEARCH_DELAY_MS = 300;

/** The furni picker of the prize form: a debounced search on the server, the matches with their icon. */
const FurniPicker: FC<{ value: string; results: { query: string; matches: { name: string; spriteId: number; typeCode: string }[] } | null; onSearch: (query: string) => void; onPick: (name: string) => void }> = ({
    value,
    results,
    onSearch,
    onPick
}) => {
    const [query, setQuery] = useState(value);
    const timer = useRef<ReturnType<typeof setTimeout>>(null);

    useEffect(() => () => timer.current && clearTimeout(timer.current), []);

    const change = (next: string) => {
        setQuery(next);

        if (timer.current) clearTimeout(timer.current);

        if (next.trim().length < 2) return;

        timer.current = setTimeout(() => onSearch(next.trim()), FURNI_SEARCH_DELAY_MS);
    };

    const matches = results && results.query === query.trim() ? results.matches : [];

    return (
        <NativeTextTree>
            <div className="octane-reward-track-admin-furni-picker" data-testid="rt-admin-furni-picker">
                <input value={query} placeholder={text('furni.search', 'Search a furni by name...')} onChange={(e) => change(e.target.value)} data-testid="rt-admin-furni-query" />
                {matches.length > 0 && (
                    <div className="octane-reward-track-admin-furni-results">
                        {matches.map((match) => (
                            <BaseButton key={match.name} type="button" className={`octane-reward-track-admin-furni-match${match.name === value ? ' is-picked' : ''}`} onClick={() => onPick(match.name)} data-testid="rt-admin-furni-match">
                                <LayoutFurniIconImageView productType={match.typeCode} productClassId={match.spriteId} className="octane-reward-track-admin-furni-icon" />
                                <span>{match.name}</span>
                            </BaseButton>
                        ))}
                    </div>
                )}
                {results && results.query === query.trim() && !matches.length && query.trim().length >= 2 && <span className="octane-reward-track-admin-muted">{text('furni.none', 'No furni with that name.')}</span>}
            </div>
        </NativeTextTree>
    );
};

/** The staff editor of the reward tracks: every stored track, its tasks with their levels, its prizes and its texts. */
export const RewardTrackAdminView: FC<{ onClose: () => void; onPreview?: (trackId: string) => void }> = ({ onClose, onPreview = null }) => {
    const {
        tracks = [],
        actionTypes = [],
        rewardTypes = [],
        loaded = false,
        pending = false,
        lastResult = null,
        furniSearch = null,
        requestData = null,
        saveTrack = null,
        saveTask = null,
        savePrize = null,
        saveTexts = null,
        deleteEntity = null,
        searchFurni = null
    } = useRewardTrackAdmin();
    const [filter, setFilter] = useState<RewardTrackAdminFilter>('all');
    const [selectedTrackId, setSelectedTrackId] = useState<string>(null);
    const [trackForm, setTrackForm] = useState<RewardTrackAdminTrackInput>(null);
    const [duplicateFrom, setDuplicateFrom] = useState<RewardTrackAdminTrack>(null);
    const [taskForm, setTaskForm] = useState<RewardTrackAdminTaskInput>(null);
    const [prizeForm, setPrizeForm] = useState<RewardTrackAdminPrizeInput>(null);
    const [textsForm, setTextsForm] = useState<Record<string, string>>(null);
    const [armed, setArmed] = useState<string>(null);

    const selectedTrack = useMemo(() => tracks.find((track) => track.id === selectedTrackId) ?? null, [tracks, selectedTrackId]);
    const visibleTracks = useMemo(() => tracks.filter((track) => filter === 'all' || getRewardTrackAdminState(track) === filter), [tracks, filter]);
    const trackErrors = useMemo(() => (trackForm ? validateRewardTrackInput(trackForm) : {}), [trackForm]);
    const taskErrors = useMemo(() => (taskForm ? validateRewardTrackTaskInput(taskForm, actionTypes) : {}), [taskForm, actionTypes]);
    const prizeErrors = useMemo(() => (prizeForm ? validateRewardTrackPrizeInput(prizeForm, rewardTypes) : {}), [prizeForm, rewardTypes]);
    const textKeys = useMemo(() => getRewardTrackTextKeys(selectedTrack ? selectedTrack.tasks.map((task) => task.id) : []), [selectedTrack]);

    useEffect(() => {
        requestData && requestData();
    }, [requestData]);

    useEffect(() => {
        if (!selectedTrackId && tracks.length) setSelectedTrackId(tracks[0].id);
    }, [tracks, selectedTrackId]);

    // A successful write closes the form it came from; the fresh list follows from the server.
    useEffect(() => {
        if (!lastResult || !lastResult.success) return;

        setArmed(null);

        if (lastResult.entity === 'task') setTaskForm(null);
        if (lastResult.entity === 'prize') setPrizeForm(null);
        if (lastResult.entity === 'texts') setTextsForm(null);
        if (lastResult.entity === 'track') {
            setTrackForm(null);
            setDuplicateFrom(null);
            setSelectedTrackId(lastResult.trackId || null);
        }
    }, [lastResult]);

    const selectTrack = (id: string) => {
        setSelectedTrackId(id);
        setTrackForm(null);
        setDuplicateFrom(null);
        setTaskForm(null);
        setPrizeForm(null);
        setTextsForm(null);
        setArmed(null);
    };

    const openTrackForm = (form: RewardTrackAdminTrackInput, source: RewardTrackAdminTrack = null) => {
        setTrackForm(form);
        setDuplicateFrom(source);
        setTaskForm(null);
        setPrizeForm(null);
        setTextsForm(null);
    };

    const submitTrack = () => {
        if (!saveTrack || !trackForm) return;

        saveTrack(trackForm);

        // A duplicate copies the tasks, prizes and texts right behind the track; the queue keeps them in order.
        if (duplicateFrom) {
            duplicateFrom.tasks.forEach((task) => saveTask && saveTask(taskToInput(trackForm.id, task)));
            duplicateFrom.prizes.forEach((prize) => savePrize && savePrize(prizeToInput(trackForm.id, prize)));

            const texts = Object.entries(duplicateFrom.texts ?? {}).map(([key, value]) => ({ key, value }));

            if (texts.length && saveTexts) saveTexts(trackForm.id, texts);
        }
    };

    const submitTexts = () => {
        if (!saveTexts || !selectedTrack || !textsForm) return;

        saveTexts(
            selectedTrack.id,
            Object.entries(textsForm)
                .filter(([, value]) => value.trim())
                .map(([key, value]) => ({ key, value: value.trim() }))
        );
    };

    const moveTask = (index: number, direction: -1 | 1) => {
        if (!selectedTrack || !saveTask) return;

        for (const changed of reorderRewardTrackEntries(selectedTrack.tasks, index, index + direction)) {
            saveTask({ ...taskToInput(selectedTrack.id, changed), sortOrder: changed.sortOrder });
        }
    };

    const updateLevel = (index: number, patch: Partial<RewardTrackAdminTaskInput['levels'][number]>) =>
        setTaskForm((form) => ({ ...form, levels: form.levels.map((level, i) => (i === index ? { ...level, ...patch } : level)) }));

    const armKey = (entity: RewardTrackAdminEntity, id: string) => `${entity}:${id}`;
    const isNewTrack = trackForm && !tracks.some((track) => track.id === trackForm.id);
    const nextSortOrder = (entries: { sortOrder: number }[]) => entries.reduce((max, entry) => Math.max(max, entry.sortOrder), 0) + 1;
    const filledTexts = selectedTrack ? Object.keys(selectedTrack.texts ?? {}).length : 0;

    return (
        <NativeTextTree>
            <div className="octane-reward-track-admin" data-testid="reward-track-admin">
                <div className="octane-reward-track-admin-toolbar">
                    <span className="octane-reward-track-admin-heading">{text('title', 'Reward track editor')}</span>
                    {lastResult && (
                        <span className={`octane-reward-track-admin-status ${lastResult.success ? 'is-ok' : 'is-error'}`} data-testid="rt-admin-status">
                            {lastResult.success ? text('saved', 'Saved, everyone online has the new track.') : lastResult.message}
                        </span>
                    )}
                    {pending && <span className="octane-reward-track-admin-status is-pending">{text('saving', 'Saving...')}</span>}
                    <Button variant="secondary" classNames={BTN} onClick={onClose}>
                        {text('close', 'Back to the track')}
                    </Button>
                </div>

                <div className="octane-reward-track-admin-body">
                    <Panel
                        className="is-tracks"
                        title={text('tracks', 'Tracks')}
                        actions={
                            <Button variant="primary" classNames={BTN} onClick={() => openTrackForm(emptyTrack())}>
                                {text('track.new', 'New')}
                            </Button>
                        }
                    >
                        <div className="octane-reward-track-admin-filters" data-testid="rt-admin-filters">
                            {FILTERS.map((entry) => (
                                <BaseButton key={entry} type="button" className={`octane-reward-track-admin-filter${filter === entry ? ' is-active' : ''}`} onClick={() => setFilter(entry)}>
                                    {text(`filter.${entry}`, entry)}
                                </BaseButton>
                            ))}
                        </div>
                        <div className="octane-reward-track-admin-track-list">
                            {!loaded && <span className="octane-reward-track-admin-muted">{text('loading', 'Loading...')}</span>}
                            {loaded && !tracks.length && <span className="octane-reward-track-admin-muted">{text('empty', 'No reward track yet.')}</span>}
                            {loaded && tracks.length > 0 && !visibleTracks.length && <span className="octane-reward-track-admin-muted">{text('filter.empty', 'No track in this state.')}</span>}
                            {visibleTracks.map((track) => (
                                <BaseButton
                                    key={track.id}
                                    type="button"
                                    className={`octane-reward-track-admin-track${track.id === selectedTrackId ? ' is-active' : ''}${track.enabled ? '' : ' is-disabled'}`}
                                    onClick={() => selectTrack(track.id)}
                                    data-testid="rt-admin-track-item"
                                >
                                    <span className="octane-reward-track-admin-track-swatch" data-theme={track.theme} />
                                    <span className="octane-reward-track-admin-track-texts">
                                        <span className="octane-reward-track-admin-track-id">{track.texts?.name ? `${track.texts.name} (${track.id})` : track.id}</span>
                                        <span className="octane-reward-track-admin-muted">
                                            {track.tasks.length} {text('tasks.short', 'tasks')} · {track.prizes.length} {text('prizes.short', 'prizes')}
                                        </span>
                                    </span>
                                    <StateTag state={getRewardTrackAdminState(track)} />
                                </BaseButton>
                            ))}
                        </div>
                    </Panel>

                    <div className="octane-reward-track-admin-detail">
                        {trackForm && (
                            <RewardTrackAdminModalView
                                title={duplicateFrom ? `${text('track.duplicate', 'Duplicate track')} · ${duplicateFrom.id}` : isNewTrack ? text('track.create', 'New track') : `${text('track.edit', 'Edit track')} · ${trackForm.id}`}
                                width={620}
                                onClose={() => openTrackForm(null)}
                            >
                                <div className="octane-reward-track-admin-form" data-testid="rt-admin-track-form">
                                    {duplicateFrom && (
                                        <span className="octane-reward-track-admin-note">
                                            {text('track.duplicate.note', 'The %count% tasks, %prizes% prizes and the texts of %source% are copied under the new id.')
                                                .replace('%count%', String(duplicateFrom.tasks.length))
                                                .replace('%prizes%', String(duplicateFrom.prizes.length))
                                                .replace('%source%', duplicateFrom.id)}
                                        </span>
                                    )}
                                    <div className="octane-reward-track-admin-grid is-3">
                                        <Field label={text('track.id', 'Id')} error={trackErrors.id}>
                                            <input value={trackForm.id} maxLength={64} placeholder="season_1" disabled={!isNewTrack} onChange={(e) => setTrackForm({ ...trackForm, id: e.target.value })} />
                                        </Field>
                                        <Field label={text('track.theme', 'Theme')} error={trackErrors.theme}>
                                            <input value={trackForm.theme} maxLength={64} placeholder="blue" onChange={(e) => setTrackForm({ ...trackForm, theme: e.target.value })} />
                                        </Field>
                                        <Field label={text('sort', 'Sort order')}>
                                            <NumberInput value={trackForm.sortOrder} onChange={(e) => setTrackForm({ ...trackForm, sortOrder: toInt(e.target.value) })} />
                                        </Field>
                                        <Field label={text('track.starts', 'Starts')}>
                                            <input type="datetime-local" value={unixToLocalInput(trackForm.startsAt)} onChange={(e) => setTrackForm({ ...trackForm, startsAt: localInputToUnix(e.target.value) })} />
                                        </Field>
                                        <Field label={text('track.ends', 'Ends')} error={trackErrors.endsAt}>
                                            <input type="datetime-local" value={unixToLocalInput(trackForm.endsAt)} onChange={(e) => setTrackForm({ ...trackForm, endsAt: localInputToUnix(e.target.value) })} />
                                        </Field>
                                    </div>
                                    <div className="octane-reward-track-admin-toggles">
                                        <Toggle label={text('track.enabled', 'Enabled: the hotel shows it')} checked={trackForm.enabled} onChange={(enabled) => setTrackForm({ ...trackForm, enabled })} />
                                        <Toggle label={text('track.premium', 'Premium pass on sale')} checked={trackForm.hasPremium} onChange={(hasPremium) => setTrackForm({ ...trackForm, hasPremium })} />
                                    </div>
                                    {trackForm.hasPremium && (
                                        <>
                                            <div className="octane-reward-track-admin-subtitle">{text('track.premium.section', 'Premium pass')}</div>
                                            <div className="octane-reward-track-admin-grid">
                                                <Field label={text('track.boost', 'Task points boost (%)')} error={trackErrors.premiumBoostPercent}>
                                                    <NumberInput min={0} value={trackForm.premiumBoostPercent} onChange={(e) => setTrackForm({ ...trackForm, premiumBoostPercent: toInt(e.target.value) })} />
                                                </Field>
                                                <Field label={text('track.instant', 'Instant points')} error={trackErrors.premiumInstantPoints}>
                                                    <NumberInput min={0} value={trackForm.premiumInstantPoints} onChange={(e) => setTrackForm({ ...trackForm, premiumInstantPoints: toInt(e.target.value) })} />
                                                </Field>
                                                <Field label={text('track.diamonds', 'Cost in diamonds')} error={trackErrors.premiumCostDiamonds}>
                                                    <NumberInput min={0} value={trackForm.premiumCostDiamonds} onChange={(e) => setTrackForm({ ...trackForm, premiumCostDiamonds: toInt(e.target.value) })} />
                                                </Field>
                                                <Field label={text('track.credits', 'Cost in credits')} error={trackErrors.premiumCostCredits}>
                                                    <NumberInput min={0} value={trackForm.premiumCostCredits} onChange={(e) => setTrackForm({ ...trackForm, premiumCostCredits: toInt(e.target.value) })} />
                                                </Field>
                                            </div>
                                        </>
                                    )}
                                    <SaveCancel disabled={pending} errorCount={hasErrors(trackErrors)} onSave={submitTrack} onCancel={() => openTrackForm(null)} />
                                </div>
                            </RewardTrackAdminModalView>
                        )}

                        {selectedTrack && (
                            <>
                                <Panel
                                    className="is-track"
                                    title={selectedTrack.texts?.name ? `${selectedTrack.texts.name} · ${selectedTrack.id}` : selectedTrack.id}
                                    actions={
                                        <>
                                            {onPreview && (
                                                <Button variant="secondary" classNames={BTN} onClick={() => onPreview(selectedTrack.id)} data-testid="rt-admin-preview">
                                                    {text('preview', 'Preview')}
                                                </Button>
                                            )}
                                            <Button variant="secondary" classNames={BTN} onClick={() => openTrackForm({ ...trackToInput(selectedTrack), id: `${selectedTrack.id}_copy`, enabled: false }, selectedTrack)} data-testid="rt-admin-duplicate-track">
                                                {text('duplicate', 'Duplicate')}
                                            </Button>
                                            <Button variant="secondary" classNames={BTN} onClick={() => setTextsForm({ ...(selectedTrack.texts ?? {}) })} data-testid="rt-admin-texts">
                                                {text('texts', 'Texts')} ({filledTexts}/{textKeys.length})
                                            </Button>
                                            <Button variant="primary" classNames={BTN} onClick={() => openTrackForm(trackToInput(selectedTrack))}>
                                                {text('edit', 'Edit')}
                                            </Button>
                                            <DeleteButton
                                                armed={armed === armKey('track', selectedTrack.id)}
                                                disabled={pending}
                                                onArm={() => setArmed(armKey('track', selectedTrack.id))}
                                                onConfirm={() => deleteEntity && deleteEntity('track', selectedTrack.id, '')}
                                            />
                                        </>
                                    }
                                >
                                    <div className="octane-reward-track-admin-facts">
                                        <StateTag state={getRewardTrackAdminState(selectedTrack)} />
                                        <span>
                                            {text('track.theme', 'Theme')}: <b>{selectedTrack.theme}</b>
                                        </span>
                                        <span>
                                            {text('sort', 'Sort order')}: <b>{selectedTrack.sortOrder}</b>
                                        </span>
                                        <span>
                                            {text('track.period', 'Period')}:{' '}
                                            <b>
                                                {selectedTrack.startsAt > 0 || selectedTrack.endsAt > 0
                                                    ? `${formatDate(selectedTrack.startsAt) || '…'} → ${formatDate(selectedTrack.endsAt) || '…'}`
                                                    : text('track.always', 'always')}
                                            </b>
                                        </span>
                                        {selectedTrack.hasPremium ? (
                                            <span>
                                                <Tag kind="premium">{text('premium', 'premium')}</Tag> ×{(selectedTrack.premiumBoostPercent / 100).toFixed(2)} · +{selectedTrack.premiumInstantPoints} {text('prize.points', 'pts')} ·{' '}
                                                {selectedTrack.premiumCostDiamonds} {text('diamonds', 'diamonds')}
                                                {selectedTrack.premiumCostCredits > 0 && ` / ${selectedTrack.premiumCostCredits} ${text('credits', 'credits')}`}
                                            </span>
                                        ) : (
                                            <span className="octane-reward-track-admin-muted">{text('track.nopremium', 'No premium pass')}</span>
                                        )}
                                    </div>
                                </Panel>

                                {textsForm && (
                                    <RewardTrackAdminModalView title={`${text('texts.edit', 'Texts')} · ${selectedTrack.id}`} width={640} onClose={() => setTextsForm(null)}>
                                        <div className="octane-reward-track-admin-form" data-testid="rt-admin-texts-form">
                                            <span className="octane-reward-track-admin-note">
                                                {text('texts.note', 'What players read in the window. An empty field falls back to the client texts (reward_track.%track%.*).').replace('%track%', selectedTrack.id)}
                                            </span>
                                            <div className="octane-reward-track-admin-text-rows">
                                                {textKeys.map(({ key, label }) => (
                                                    <label key={key} className="octane-reward-track-admin-text-row">
                                                        <span className="octane-reward-track-admin-label" title={`reward_track.${selectedTrack.id}.${key}`}>
                                                            {label}
                                                        </span>
                                                        <input value={textsForm[key] ?? ''} maxLength={1000} data-testid={`rt-admin-text-${key}`} onChange={(e) => setTextsForm({ ...textsForm, [key]: e.target.value })} />
                                                    </label>
                                                ))}
                                            </div>
                                            <SaveCancel disabled={pending} errorCount={0} onSave={submitTexts} onCancel={() => setTextsForm(null)} />
                                        </div>
                                    </RewardTrackAdminModalView>
                                )}

                                <Panel
                                    collapseKey="tasks"
                                    title={`${text('tasks', 'Tasks')} (${selectedTrack.tasks.length})`}
                                    summary={selectedTrack.tasks.map((task) => task.id).join(' · ')}
                                    actions={
                                        <Button variant="primary" classNames={BTN} onClick={() => setTaskForm(emptyTask(selectedTrack.id, actionTypes[0] ?? '', nextSortOrder(selectedTrack.tasks)))}>
                                            {text('task.new', 'New task')}
                                        </Button>
                                    }
                                >
                                    {selectedTrack.tasks.length > 0 && (
                                        <table className="octane-reward-track-admin-table">
                                            <thead>
                                                <tr>
                                                    <th className="octane-reward-track-admin-order-head" />
                                                    <th>{text('task.id', 'Id')}</th>
                                                    <th>{text('task.action', 'Action')}</th>
                                                    <th>{text('task.levels', 'Levels (count → points)')}</th>
                                                    <th />
                                                </tr>
                                            </thead>
                                            <tbody>
                                                {selectedTrack.tasks.map((task, index) => (
                                                    <tr key={task.id} data-testid="rt-admin-task-row">
                                                        <td className="octane-reward-track-admin-order">
                                                            <Hint content={text('move.up', 'Move up')}><BaseButton aria-label={text('move.up', 'Move up')} type="button" className="octane-reward-track-admin-arrow" disabled={pending || index === 0} data-testid="rt-admin-task-up" onClick={() => moveTask(index, -1)}>
                                                                ▲
                                                            </BaseButton></Hint>
                                                            <Hint content={text('move.down', 'Move down')}><BaseButton aria-label={text('move.down', 'Move down')}
                                                                type="button"
                                                                className="octane-reward-track-admin-arrow"
                                                                disabled={pending || index === selectedTrack.tasks.length - 1}
                                                                data-testid="rt-admin-task-down"
                                                                onClick={() => moveTask(index, 1)}
                                                            >
                                                                ▼
                                                            </BaseButton></Hint>
                                                        </td>
                                                        <td>
                                                            <b>{task.id}</b>
                                                            {selectedTrack.texts?.[`task.${task.id}.name`] && <span className="octane-reward-track-admin-muted"> {selectedTrack.texts[`task.${task.id}.name`]}</span>}
                                                            {task.premium && <Tag kind="premium">{text('premium', 'premium')}</Tag>}
                                                        </td>
                                                        <td>
                                                            {task.actionType}
                                                            {task.parameter && <span className="octane-reward-track-admin-muted"> ({task.parameter})</span>}
                                                        </td>
                                                        <td className="octane-reward-track-admin-chips">
                                                            {task.levels.map((level, levelIndex) => (
                                                                <span key={levelIndex} className={`octane-reward-track-admin-chip${level.premium ? ' is-premium' : ''}`}>
                                                                    {level.requiredCount} → {level.pointsReward}
                                                                </span>
                                                            ))}
                                                        </td>
                                                        <td className="octane-reward-track-admin-row-actions">
                                                            <Button variant="secondary" classNames={BTN} onClick={() => setTaskForm(taskToInput(selectedTrack.id, task))}>
                                                                {text('edit', 'Edit')}
                                                            </Button>
                                                            <DeleteButton
                                                                armed={armed === armKey('task', task.id)}
                                                                disabled={pending}
                                                                onArm={() => setArmed(armKey('task', task.id))}
                                                                onConfirm={() => deleteEntity && deleteEntity('task', selectedTrack.id, task.id)}
                                                            />
                                                        </td>
                                                    </tr>
                                                ))}
                                            </tbody>
                                        </table>
                                    )}
                                </Panel>

                                {taskForm && (
                                    <RewardTrackAdminModalView
                                        title={taskForm.id && selectedTrack.tasks.some((task) => task.id === taskForm.id) ? `${text('task.edit', 'Edit task')} · ${taskForm.id}` : text('task.create', 'New task')}
                                        width={620}
                                        onClose={() => setTaskForm(null)}
                                    >
                                        <div className="octane-reward-track-admin-form" data-testid="rt-admin-task-form">
                                            <div className="octane-reward-track-admin-grid">
                                                <Field label={text('task.id', 'Id')} error={taskErrors.id}>
                                                    <input value={taskForm.id} maxLength={64} placeholder="talk" onChange={(e) => setTaskForm({ ...taskForm, id: e.target.value })} />
                                                </Field>
                                                <Field label={text('task.action', 'Action')} error={taskErrors.actionType}>
                                                    <Select value={taskForm.actionType} onChange={(e) => setTaskForm({ ...taskForm, actionType: e.target.value })}>
                                                        {actionTypes.map((actionType) => (
                                                            <option key={actionType} value={actionType}>
                                                                {actionType}
                                                            </option>
                                                        ))}
                                                    </Select>
                                                </Field>
                                                <Field label={text('task.parameter', 'Parameter')} error={taskErrors.parameter}>
                                                    <input value={taskForm.parameter} maxLength={255} onChange={(e) => setTaskForm({ ...taskForm, parameter: e.target.value })} />
                                                </Field>
                                                <Field label={text('sort', 'Sort order')}>
                                                    <NumberInput value={taskForm.sortOrder} onChange={(e) => setTaskForm({ ...taskForm, sortOrder: toInt(e.target.value) })} />
                                                </Field>
                                            </div>
                                            <div className="octane-reward-track-admin-toggles">
                                                <Toggle label={text('task.premium', 'Premium pass holders only')} checked={taskForm.premium} onChange={(premium) => setTaskForm({ ...taskForm, premium })} />
                                            </div>
                                            <div className="octane-reward-track-admin-subtitle">
                                                {text('task.levels.section', 'Levels')}
                                                {taskErrors.levels && <span className="octane-reward-track-admin-field-error"> · {taskErrors.levels}</span>}
                                            </div>
                                            <table className="octane-reward-track-admin-table is-levels">
                                                <thead>
                                                    <tr>
                                                        <th>#</th>
                                                        <th>{text('level.count', 'Required count')}</th>
                                                        <th>{text('level.points', 'Points')}</th>
                                                        <th>{text('level.premium', 'Premium only')}</th>
                                                        <th />
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                    {taskForm.levels.map((level, index) => (
                                                        <tr key={index} data-testid="rt-admin-level-row">
                                                            <td className="octane-reward-track-admin-muted">{index + 1}</td>
                                                            <td>
                                                                <NumberInput min={1} value={level.requiredCount} aria-invalid={!!taskErrors[`level.${index}.requiredCount`]} title={taskErrors[`level.${index}.requiredCount`]} onChange={(e) => updateLevel(index, { requiredCount: toInt(e.target.value, 1) })} />
                                                                {taskErrors[`level.${index}.requiredCount`] && <span className="octane-reward-track-admin-field-error">{taskErrors[`level.${index}.requiredCount`]}</span>}
                                                            </td>
                                                            <td>
                                                                <NumberInput min={0} value={level.pointsReward} aria-invalid={!!taskErrors[`level.${index}.pointsReward`]} onChange={(e) => updateLevel(index, { pointsReward: toInt(e.target.value) })} />
                                                                {taskErrors[`level.${index}.pointsReward`] && <span className="octane-reward-track-admin-field-error">{taskErrors[`level.${index}.pointsReward`]}</span>}
                                                            </td>
                                                            <td>
                                                                <Checkbox checked={level.premium} onCheckedChange={(nextChecked) => updateLevel(index, { premium: nextChecked })} />
                                                            </td>
                                                            <td className="octane-reward-track-admin-row-actions">
                                                                <Button
                                                                    variant="secondary"
                                                                    classNames={BTN}
                                                                    disabled={taskForm.levels.length <= 1}
                                                                    onClick={() => setTaskForm({ ...taskForm, levels: taskForm.levels.filter((_, i) => i !== index) })}
                                                                >
                                                                    {text('level.remove', 'Remove')}
                                                                </Button>
                                                            </td>
                                                        </tr>
                                                    ))}
                                                </tbody>
                                            </table>
                                            <SaveCancel
                                                disabled={pending}
                                                errorCount={hasErrors(taskErrors)}
                                                onSave={() => saveTask && saveTask(taskForm)}
                                                onCancel={() => setTaskForm(null)}
                                                before={
                                                    <Button
                                                        variant="secondary"
                                                        classNames={BTN}
                                                        onClick={() => {
                                                            const last = taskForm.levels[taskForm.levels.length - 1];

                                                            setTaskForm({ ...taskForm, levels: [...taskForm.levels, { requiredCount: (last?.requiredCount ?? 0) + 1, pointsReward: last?.pointsReward ?? 10, premium: false }] });
                                                        }}
                                                    >
                                                        {text('level.add', 'Add level')}
                                                    </Button>
                                                }
                                            />
                                        </div>
                                    </RewardTrackAdminModalView>
                                )}

                                <Panel
                                    collapseKey="prizes"
                                    title={`${text('prizes', 'Prizes')} (${selectedTrack.prizes.length})`}
                                    summary={`${selectedTrack.prizes.filter((prize) => !prize.premium).length} ${text('free', 'free')} · ${selectedTrack.prizes.filter((prize) => prize.premium).length} ${text('premium', 'premium')}`}
                                    actions={
                                        <Button variant="primary" classNames={BTN} onClick={() => setPrizeForm(emptyPrize(selectedTrack.id, rewardTypes[0] ?? '', nextSortOrder(selectedTrack.prizes)))}>
                                            {text('prize.new', 'New prize')}
                                        </Button>
                                    }
                                >
                                    {selectedTrack.prizes.length > 0 && (
                                        <table className="octane-reward-track-admin-table">
                                            <thead>
                                                <tr>
                                                    <th>{text('prize.id', 'Id')}</th>
                                                    <th>{text('prize.required', 'Points')}</th>
                                                    <th>{text('prize.reward', 'Reward')}</th>
                                                    <th>{text('prize.row', 'Row')}</th>
                                                    <th title={text('prize.claimed.title', 'How many users claimed it')}>{text('prize.claimed', 'Claimed')}</th>
                                                    <th />
                                                </tr>
                                            </thead>
                                            <tbody>
                                                {selectedTrack.prizes.map((prize) => (
                                                    <tr key={prize.id} data-testid="rt-admin-prize-row">
                                                        <td>
                                                            <b>{prize.id}</b>
                                                        </td>
                                                        <td>{prize.requiredPoints}</td>
                                                        <td>
                                                            {prize.rewardAmount} {prize.rewardType}
                                                            {prize.extraParams && <span className="octane-reward-track-admin-muted"> ({prize.extraParams})</span>}
                                                        </td>
                                                        <td>{prize.premium ? <Tag kind="premium">{text('premium', 'premium')}</Tag> : <Tag>{text('free', 'free')}</Tag>}</td>
                                                        <td data-testid="rt-admin-prize-claimed">{prize.claimedCount ?? 0}</td>
                                                        <td className="octane-reward-track-admin-row-actions">
                                                            <Button variant="secondary" classNames={BTN} title={text('prize.twin.title', 'A copy on the other row, 20 points apart')} onClick={() => setPrizeForm(prizeTwin(selectedTrack.id, prize))} data-testid="rt-admin-prize-twin">
                                                                {text('prize.twin', 'Twin')}
                                                            </Button>
                                                            <Button variant="secondary" classNames={BTN} onClick={() => setPrizeForm(prizeToInput(selectedTrack.id, prize))}>
                                                                {text('edit', 'Edit')}
                                                            </Button>
                                                            <DeleteButton
                                                                armed={armed === armKey('prize', prize.id)}
                                                                disabled={pending}
                                                                onArm={() => setArmed(armKey('prize', prize.id))}
                                                                onConfirm={() => deleteEntity && deleteEntity('prize', selectedTrack.id, prize.id)}
                                                            />
                                                        </td>
                                                    </tr>
                                                ))}
                                            </tbody>
                                        </table>
                                    )}
                                </Panel>

                                {prizeForm && (
                                    <RewardTrackAdminModalView
                                        title={prizeForm.id && selectedTrack.prizes.some((prize) => prize.id === prizeForm.id) ? `${text('prize.edit', 'Edit prize')} · ${prizeForm.id}` : text('prize.create', 'New prize')}
                                        width={620}
                                        onClose={() => setPrizeForm(null)}
                                    >
                                        <div className="octane-reward-track-admin-form" data-testid="rt-admin-prize-form">
                                            <div className="octane-reward-track-admin-grid">
                                                <Field label={text('prize.id', 'Id')} error={prizeErrors.id}>
                                                    <input value={prizeForm.id} maxLength={64} placeholder="p1" onChange={(e) => setPrizeForm({ ...prizeForm, id: e.target.value })} />
                                                </Field>
                                                <Field label={text('prize.required', 'Required points')} error={prizeErrors.requiredPoints}>
                                                    <NumberInput min={0} value={prizeForm.requiredPoints} onChange={(e) => setPrizeForm({ ...prizeForm, requiredPoints: toInt(e.target.value) })} />
                                                </Field>
                                                <Field label={text('prize.type', 'Reward type')} error={prizeErrors.rewardType}>
                                                    <Select value={prizeForm.rewardType} onChange={(e) => setPrizeForm({ ...prizeForm, rewardType: e.target.value })}>
                                                        {rewardTypes.map((rewardType) => (
                                                            <option key={rewardType} value={rewardType}>
                                                                {rewardType}
                                                            </option>
                                                        ))}
                                                    </Select>
                                                </Field>
                                                <Field label={text('prize.amount', 'Amount')} error={prizeErrors.rewardAmount}>
                                                    <NumberInput min={0} value={prizeForm.rewardAmount} onChange={(e) => setPrizeForm({ ...prizeForm, rewardAmount: toInt(e.target.value) })} />
                                                </Field>
                                                <Field
                                                    label={prizeForm.rewardType === 'furni' ? text('prize.extra.furni', 'Furni name (items_base.item_name)') : text('prize.extra', 'Extra (badge code)')}
                                                    error={prizeErrors.extraParams}
                                                    span={2}
                                                >
                                                    <input
                                                        value={prizeForm.extraParams}
                                                        maxLength={255}
                                                        placeholder={prizeForm.rewardType === 'badge' ? 'ACH_Login1' : prizeForm.rewardType === 'furni' ? 'club_sofa' : ''}
                                                        onChange={(e) => setPrizeForm({ ...prizeForm, extraParams: e.target.value })}
                                                    />
                                                </Field>
                                                <Field label={text('prize.product', 'Product type id')} error={prizeErrors.productItemTypeId}>
                                                    <NumberInput min={0} max={32767} value={prizeForm.productItemTypeId} onChange={(e) => setPrizeForm({ ...prizeForm, productItemTypeId: toInt(e.target.value) })} />
                                                </Field>
                                                <Field label={text('sort', 'Sort order')}>
                                                    <NumberInput value={prizeForm.sortOrder} onChange={(e) => setPrizeForm({ ...prizeForm, sortOrder: toInt(e.target.value) })} />
                                                </Field>
                                            </div>
                                            {prizeForm.rewardType === 'furni' && searchFurni && (
                                                <FurniPicker value={prizeForm.extraParams} results={furniSearch} onSearch={searchFurni} onPick={(name) => setPrizeForm({ ...prizeForm, extraParams: name })} />
                                            )}
                                            <div className="octane-reward-track-admin-toggles">
                                                <Toggle label={text('prize.premium', 'On the premium row')} checked={prizeForm.premium} onChange={(premium) => setPrizeForm({ ...prizeForm, premium })} />
                                            </div>
                                            <SaveCancel disabled={pending} errorCount={hasErrors(prizeErrors)} onSave={() => savePrize && savePrize(prizeForm)} onCancel={() => setPrizeForm(null)} />
                                        </div>
                                    </RewardTrackAdminModalView>
                                )}
                            </>
                        )}
                    </div>
                </div>
            </div>
        </NativeTextTree>
    );
};
