import {
    AlertDialog,
    AlertDialogAction,
    AlertDialogCancel,
    AlertDialogContent,
    AlertDialogDescription,
    AlertDialogFooter,
    AlertDialogHeader,
    AlertDialogTitle,
    AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { useTranslation } from '@/hooks/use-translation';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, Link, router, usePage } from '@inertiajs/react';
import { AlertTriangle, PlusIcon, Trash2Icon } from 'lucide-react';
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { toast } from 'sonner';
import AdditionalFeesEditor from './components/additional-fees-editor';
import BulkProgressPanel from './components/bulk-progress-panel';
import CancellationPoliciesEditor from './components/cancellation-policies-editor';
import CheckInOutTimesFields from './components/check-in-out-times-fields';
import PreparationTimeField from './components/preparation-time-field';
import PropertyPicker from './components/property-picker';
import SeasonFormFields from './components/season-form-fields';
import SeasonOverlapDialog from './components/season-overlap-dialog';
import SeasonTemplateGallery, {
    INTENSITY_BAR_CLASS,
    TIMELINE_BASE_PRICE_CLASS,
    TIMELINE_MANUAL_CLASS,
    TIMELINE_UNTOUCHED_CLASS,
} from './components/season-template-gallery';
import SeasonTimeline, { type TimelineSegment } from './components/season-timeline';
import SectionSidebar from './components/section-sidebar';
import { FEE_COLLECT_TIMES, FEE_DISCRIMINATORS_WITH_APPLIED_TO } from './constants';
import {
    addOneYear,
    computeGaps,
    findOverlappingSeasons,
    hasOverlap,
    resolveSeasonConflicts,
    seasonRangeKey,
    toLocalISODate,
    type OverlapWinner,
    type SeasonTemplate,
} from './season-range-utils';
import type { AdditionalFee, CancellationPolicy, FeeDiscriminator, FeeType, Season } from './types';
import useBulkApply, { type BulkJob } from './use-bulk-apply';

/** A paused overlap fold over the bulk price rows, awaiting a resolution choice. */
type BulkResolve = {
    remaining: Season[];
    acc: Season[];
    current: Season;
    conflicts: Season[];
    ci: number;
    winners: Record<string, OverlapWinner>;
    autoWinner: OverlapWinner | null;
};

type PropertyOption = {
    uid: string;
    name: string | null;
    internal_name: string | null;
    image: string | null;
    location: string | null;
    type: string | null;
    owner: string | null;
    current_price: number | null;
    street: string | null;
};

type PageProps = {
    properties: PropertyOption[];
    feeTypes: FeeType[];
    feeDiscriminators: FeeDiscriminator[];
    /** Recurring season templates from config/season-templates.php. */
    seasonTemplates?: SeasonTemplate[];
};

type CheckInOutValue = {
    check_in_from: string;
    check_in_to: string;
    check_out_until: string;
    place: string;
};

type EnabledFields = {
    check_in_out: boolean;
    preparation_time: boolean;
    cancellation_policies: boolean;
    additional_fees: boolean;
    price: boolean;
};

/** Either the jobs of a section's enabled sub-blocks, or a validation error message. */
type SectionJobsResult = { jobs: BulkJob[] } | { error: string };

const SELECTION_SECTION = 3;

/** Hard cap on price ranges per property (seasons + base-price gaps) — keeps the RU payload sane. */
const MAX_PRICE_RANGES = 40;

/**
 * Sub-block wrapper: a "modify this field" toggle that keeps the inner inputs
 * greyed out and inert until the user explicitly opts in (bulk updates replace
 * the whole value on every selected property).
 */
function FieldToggleBlock({
    enabled,
    onToggle,
    disabled,
    children,
}: {
    enabled: boolean;
    onToggle: (next: boolean) => void;
    disabled?: boolean;
    children: ReactNode;
}) {
    const { t } = useTranslation();

    return (
        <div className="rounded-lg border p-4">
            <label className="flex w-fit cursor-pointer items-center gap-2 text-sm font-medium">
                <Checkbox checked={enabled} onCheckedChange={(v) => onToggle(v === true)} disabled={disabled} />
                {t('properties.bulk-edit', 'modify_this_field')}
            </label>
            <div className={enabled ? 'mt-2' : 'pointer-events-none mt-2 opacity-50 select-none'} aria-disabled={!enabled}>
                {children}
            </div>
        </div>
    );
}

export default function BulkEdit() {
    const { properties, feeTypes, feeDiscriminators, seasonTemplates } = usePage<PageProps>().props;
    const { t } = useTranslation();

    // Server config (config/season-templates.php) is the single source of truth.
    const templates = seasonTemplates ?? [];

    const breadcrumbs: BreadcrumbItem[] = [
        {
            title: t('properties.index', 'accommodation_management'),
            href: '/logements',
        },
        {
            title: t('properties.bulk-edit', 'page_title'),
            href: '/logements/bulk-edit',
        },
    ];

    const bulk = useBulkApply();

    // ---- Sections --------------------------------------------------------
    const sections = [
        {
            title: t('properties.index', 'check_in_out'),
            description: t('properties.index', 'check_in_out_desc'),
        },
        {
            title: t('properties.index', 'payment_and_deposit'),
            description: t('properties.index', 'payment_and_deposit_desc'),
        },
        {
            title: t('properties.bulk-edit', 'section_price'),
            description: t('properties.bulk-edit', 'section_price_desc'),
        },
        {
            title: t('properties.bulk-edit', 'section_selection'),
            description: t('properties.bulk-edit', 'section_selection_desc'),
        },
    ];

    const [currentSection, setCurrentSection] = useState(0);
    const [selectedUids, setSelectedUids] = useState<string[]>([]);

    // ---- Editors state ---------------------------------------------------
    const [enabled, setEnabled] = useState<EnabledFields>({
        check_in_out: false,
        preparation_time: false,
        cancellation_policies: false,
        additional_fees: false,
        price: false,
    });
    const [checkInOut, setCheckInOut] = useState<CheckInOutValue>({ check_in_from: '', check_in_to: '', check_out_until: '', place: 'apartment' });
    const [preparationTime, setPreparationTime] = useState<string | number>('');
    const [policies, setPolicies] = useState<CancellationPolicy[]>([]);
    const [fees, setFees] = useState<AdditionalFee[]>([]);
    const [seasons, setSeasons] = useState<Season[]>([]);
    const [baseEnabled, setBaseEnabled] = useState(false);
    const [basePrice, setBasePrice] = useState<string>('');
    // Index of the season row whose price input should grab focus on mount
    // (set when a template chip pre-fills everything but the price).
    const [focusPriceIndex, setFocusPriceIndex] = useState<number | null>(null);

    // Pricing window [today → today + 1 year]; computed once per page mount.
    const today = useMemo(() => toLocalISODate(new Date()), []);
    const horizon = useMemo(() => addOneYear(today), [today]);

    const toggleField = (field: keyof EnabledFields, next: boolean) => setEnabled((prev) => ({ ...prev, [field]: next }));

    const anyFieldEnabled = Object.values(enabled).some(Boolean);

    // Sidebar: edit sections turn green as soon as they carry at least one
    // enabled field (a map of "what will be applied"); the selection section
    // turns green once at least one property is checked.
    const sectionStates: boolean[] = [
        enabled.check_in_out || enabled.preparation_time,
        enabled.cancellation_policies || enabled.additional_fees,
        enabled.price,
        selectedUids.length > 0,
    ];

    // ---- Confirmation dialog ----------------------------------------------
    const [confirmOpen, setConfirmOpen] = useState(false);
    const [pendingJobs, setPendingJobs] = useState<BulkJob[]>([]);

    // Block in-app navigation while a run is in progress (the chunked POSTs of
    // the run itself are not GET visits, so they pass through). The listener is
    // removed when the run ends or the page unmounts.
    useEffect(() => {
        if (!bulk.running) {
            return;
        }

        return router.on('before', (event) => {
            if (event.detail.visit.method === 'get') {
                event.preventDefault();
            }
        });
    }, [bulk.running]);

    // ---- Job builders ------------------------------------------------------
    const isFilledNumber = (v: number | string | undefined): boolean => v !== '' && v !== undefined && !Number.isNaN(Number(v));

    const buildSectionJobs = (section: number): SectionJobsResult => {
        const jobs: BulkJob[] = [];

        if (section === 0) {
            if (enabled.check_in_out) {
                if (!checkInOut.check_in_from || !checkInOut.check_in_to || !checkInOut.check_out_until || !checkInOut.place) {
                    return { error: t('properties.step6', 'fill_all_required_fields') };
                }
                jobs.push({ field: 'check_in_out', value: { ...checkInOut } });
            }
            if (enabled.preparation_time) {
                const hours = parseInt(String(preparationTime), 10);
                if (Number.isNaN(hours) || hours < 0) {
                    return { error: t('properties.step6', 'fill_all_required_fields') };
                }
                jobs.push({ field: 'preparation_time', value: { hours } });
            }
        }

        if (section === 1) {
            if (enabled.cancellation_policies) {
                const clean = policies
                    .filter((p) => isFilledNumber(p.valid_from) && isFilledNumber(p.valid_to) && isFilledNumber(p.charge))
                    .map((p) => ({
                        valid_from: parseInt(String(p.valid_from), 10),
                        valid_to: parseInt(String(p.valid_to), 10),
                        charge: parseFloat(String(p.charge)),
                    }));
                if (clean.length === 0) {
                    return { error: t('properties.create-edit', 'cancellation_policy_required') };
                }
                jobs.push({ field: 'cancellation_policies', value: { policies: clean } });
            }
            if (enabled.additional_fees) {
                const clean = fees
                    .filter((f) => f.typeId !== '' && f.discriminatorId !== '' && f.name !== '' && isFilledNumber(f.amount))
                    .map((f) => ({
                        name: f.name,
                        discriminatorId: parseInt(String(f.discriminatorId), 10),
                        typeId: parseInt(String(f.typeId), 10),
                        collectTime: parseInt(String(f.collectTime), 10) || parseInt(FEE_COLLECT_TIMES.AT_BOOKING, 10),
                        amount: parseFloat(String(f.amount)),
                        optional: f.optional,
                        ...(FEE_DISCRIMINATORS_WITH_APPLIED_TO.includes(String(f.discriminatorId))
                            ? { rent: f.rent ?? false, cleaning_fee: f.cleaning_fee ?? false }
                            : {}),
                    }));
                if (clean.length === 0) {
                    return { error: t('properties.create-edit', 'additional_fee_invalid') };
                }
                jobs.push({ field: 'additional_fees', value: { fees: clean } });
            }
        }

        if (section === 2 && enabled.price) {
            const baseValue = parseFloat(basePrice);
            if (baseEnabled && (basePrice.trim() === '' || Number.isNaN(baseValue) || baseValue < 0)) {
                return { error: t('properties.step6', 'base_price_invalid') };
            }
            // With an active base price, zero typed seasons is legitimate
            // (single year-round price: one gap covering the whole window).
            if (seasons.length === 0 && !baseEnabled) {
                return { error: t('properties.step6', 'add_at_least_one_season') };
            }
            for (const season of seasons) {
                if (!season.start_date || !season.end_date || !isFilledNumber(season.daily_price)) {
                    return { error: t('properties.step6', 'fill_all_required_fields') };
                }
                if (new Date(season.start_date) > new Date(season.end_date)) {
                    return { error: t('properties.step6', 'start_date_before_end_date') };
                }
            }
            // Les chevauchements sont résolus (rognage/découpe via le dialogue)
            // AVANT d'arriver ici, par handleApplyClick → la table est propre.
            const ranges = seasons.map((s) => ({
                start_date: s.start_date,
                end_date: s.end_date,
                daily_price: parseFloat(String(s.daily_price)),
                extra_price: isFilledNumber(s.extra_price) ? Number(s.extra_price) : null,
                name: s.name || null,
            }));
            if (baseEnabled) {
                // Base price fills the stretches of [today → today + 1 year] not
                // covered by a season: seasons keep priority, gaps are disjoint
                // from them by construction (no extra overlap check needed).
                for (const gap of computeGaps(today, horizon, seasons)) {
                    ranges.push({ ...gap, daily_price: baseValue, extra_price: null, name: null });
                }
            }
            if (ranges.length > MAX_PRICE_RANGES) {
                return {
                    error: t('properties.step6', 'too_many_ranges')
                        .replace(':count', String(ranges.length))
                        .replace(':max', String(MAX_PRICE_RANGES)),
                };
            }
            jobs.push({ field: 'price', value: { seasons: ranges } });
        }

        return { jobs };
    };

    /**
     * Concatenates the jobs of every edit section. On a validation error,
     * jumps to the faulty section before showing the toast and returns null.
     */
    const buildAllJobs = (): BulkJob[] | null => {
        const jobs: BulkJob[] = [];
        for (const section of [0, 1, 2]) {
            const result = buildSectionJobs(section);
            if ('error' in result) {
                setCurrentSection(section);
                toast.warning(result.error);
                return null;
            }
            jobs.push(...result.jobs);
        }
        return jobs;
    };

    const doApply = () => {
        const jobs = buildAllJobs();
        if (jobs === null) {
            return; // a validation toast was already shown
        }
        if (jobs.length === 0) {
            toast.warning(t('properties.bulk-edit', 'nothing_selected_to_apply'));
            return;
        }
        setPendingJobs(jobs);
        setConfirmOpen(true);
    };

    // ---- Overlap resolution (same trim/split dialog as the single editor) ----
    const [bulkResolve, setBulkResolve] = useState<BulkResolve | null>(null);
    const [pendingApply, setPendingApply] = useState(false);
    const bulkAfter = useRef<(() => void) | null>(null);

    // Fold the rows into a clean, non-overlapping list; pause on a dialog when a
    // conflict needs a choice, resume on resolve. `autoWinner` (set by the "apply
    // to rest" checkbox) resolves every further conflict silently.
    const driveBulk = (
        remaining: Season[],
        acc: Season[],
        autoWinner: OverlapWinner | null,
        current: Season | null,
        conflicts: Season[],
        ci: number,
        winners: Record<string, OverlapWinner>,
    ) => {
        for (;;) {
            if (!current) {
                if (remaining.length === 0) {
                    setSeasons(acc);
                    setBulkResolve(null);
                    const after = bulkAfter.current;
                    bulkAfter.current = null;
                    after?.();
                    return;
                }
                current = remaining[0];
                remaining = remaining.slice(1);
                conflicts = findOverlappingSeasons(current, acc);
                ci = 0;
                winners = {};
            }
            const cur: Season = current;
            if (ci < conflicts.length) {
                if (autoWinner) {
                    winners = { ...winners, [seasonRangeKey(conflicts[ci])]: autoWinner };
                    ci += 1;
                    continue;
                }
                setBulkResolve({ remaining, acc, current: cur, conflicts, ci, winners, autoWinner });
                return;
            }
            acc = resolveSeasonConflicts(cur, acc, winners);
            current = null;
        }
    };

    const resolveBulkConflict = (winner: OverlapWinner, applyToRest: boolean) => {
        const s = bulkResolve;
        if (!s) return;
        const winners = { ...s.winners, [seasonRangeKey(s.conflicts[s.ci])]: winner };
        driveBulk(s.remaining, s.acc, applyToRest ? winner : s.autoWinner, s.current, s.conflicts, s.ci + 1, winners);
    };

    const cancelBulkResolve = () => {
        bulkAfter.current = null;
        setBulkResolve(null);
    };

    // Only coherent, fully-typed rows take part in overlap resolution — an
    // incomplete row (still being typed) must reach buildSectionJobs' own
    // "fill all fields" check, not get stuck looking like an overlap.
    const overlapNeedsResolution = (list: Season[]) =>
        hasOverlap(list.filter((s) => !!s.start_date && !!s.end_date && s.start_date <= s.end_date));

    // Once the fold has cleaned `seasons`, the effect resumes the apply.
    useEffect(() => {
        if (pendingApply && !overlapNeedsResolution(seasons)) {
            setPendingApply(false);
            doApply();
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [pendingApply, seasons]);

    const handleApplyClick = () => {
        // Resolve overlapping price rows first (trim/split via the dialog), then
        // apply once the table is clean.
        if (enabled.price && overlapNeedsResolution(seasons)) {
            bulkAfter.current = () => setPendingApply(true);
            driveBulk([...seasons], [], null, null, [], 0, {});
            return;
        }
        doApply();
    };

    const handleConfirm = () => {
        setConfirmOpen(false);
        bulk.start(pendingJobs, selectedUids);
    };

    // ---- Seasons (price section) -------------------------------------------
    const handleAddSeason = () => {
        setFocusPriceIndex(null);
        setSeasons([...seasons, { name: '', start_date: '', end_date: '', daily_price: '', extra_price: undefined }]);
    };

    /** Template chip click: row pre-filled with projected dates, only the price is left. */
    const handlePickTemplate = (picked: { name: string; start_date: string; end_date: string }) => {
        setFocusPriceIndex(seasons.length);
        setSeasons([...seasons, { ...picked, daily_price: '', extra_price: undefined }]);
    };

    const handleSeasonChange = (index: number, patch: Partial<Season>) => {
        setSeasons(seasons.map((s, i) => (i === index ? { ...s, ...patch } : s)));
    };

    const removeSeason = (index: number) => {
        setFocusPriceIndex(null);
        setSeasons(seasons.filter((_, i) => i !== index));
    };

    // ---- Live coverage preview (gallery conflicts + timeline) ----------------
    // Only rows with a coherent, fully-typed date range take part.
    const completeRanges = seasons.filter((s) => s.start_date && s.end_date && s.start_date <= s.end_date);

    const basePriceValid = baseEnabled && basePrice.trim() !== '' && !Number.isNaN(Number(basePrice)) && Number(basePrice) >= 0;

    const templateByName = new Map(templates.map((tpl) => [tpl.name, tpl]));
    const timelineSegments: TimelineSegment[] = [
        // Typed seasons: template color when the name matches one, neutral otherwise.
        ...completeRanges.map((s) => {
            const tpl = s.name ? templateByName.get(s.name) : undefined;
            const price = isFilledNumber(s.daily_price) ? ` · ${s.daily_price}` : '';
            return {
                start_date: s.start_date,
                end_date: s.end_date,
                color: tpl ? INTENSITY_BAR_CLASS[tpl.intensity] : TIMELINE_MANUAL_CLASS,
                label: `${s.name || t('properties.step6', 'timeline_manual_season')}${price}`,
            };
        }),
        // Uncovered stretches: base price fill when active, "untouched" outline otherwise.
        ...computeGaps(today, horizon, completeRanges).map((gap) => ({
            ...gap,
            color: basePriceValid ? TIMELINE_BASE_PRICE_CLASS : TIMELINE_UNTOUCHED_CLASS,
            label: basePriceValid
                ? `${t('properties.step6', 'timeline_base_price')} · ${basePrice}`
                : t('properties.step6', 'timeline_not_modified'),
        })),
    ];

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title={t('properties.bulk-edit', 'page_title')} />

            <div className="px-4 pt-4">
                <div className="flex flex-wrap items-center justify-between gap-2 rounded-lg border p-4">
                    <h1 className="text-lg font-bold">
                        {t('properties.bulk-edit', 'page_title')} — {selectedUids.length} {t('properties.bulk-edit', 'editing_count')}
                    </h1>
                    <Link href={route('logements')} className="text-secondary text-sm underline underline-offset-4">
                        {t('properties.bulk-edit', 'back_to_list')}
                    </Link>
                </div>
            </div>

            <div className="mt-4 flex flex-col gap-1 p-4 md:flex-row">
                <SectionSidebar
                    steps={sections.map((section, index) => ({
                        title: section.title,
                        description: section.description,
                        state: sectionStates[index] ? 'valid' : 'neutral',
                    }))}
                    currentIndex={currentSection}
                    onSelect={(index) => {
                        if (!bulk.running) {
                            setCurrentSection(index);
                        }
                    }}
                />

                <div className="w-full px-2 md:w-3/4">
                    {currentSection === 0 && (
                        <div className="flex flex-col gap-4">
                            <FieldToggleBlock
                                enabled={enabled.check_in_out}
                                onToggle={(next) => toggleField('check_in_out', next)}
                                disabled={bulk.running}
                            >
                                <CheckInOutTimesFields value={checkInOut} onChange={(patch) => setCheckInOut({ ...checkInOut, ...patch })} />
                            </FieldToggleBlock>

                            <FieldToggleBlock
                                enabled={enabled.preparation_time}
                                onToggle={(next) => toggleField('preparation_time', next)}
                                disabled={bulk.running}
                            >
                                <PreparationTimeField value={preparationTime} onChange={setPreparationTime} />
                            </FieldToggleBlock>
                        </div>
                    )}

                    {currentSection === 1 && (
                        <div className="flex flex-col gap-4">
                            <FieldToggleBlock
                                enabled={enabled.cancellation_policies}
                                onToggle={(next) => toggleField('cancellation_policies', next)}
                                disabled={bulk.running}
                            >
                                <CancellationPoliciesEditor policies={policies} onChange={setPolicies} />
                            </FieldToggleBlock>

                            <FieldToggleBlock
                                enabled={enabled.additional_fees}
                                onToggle={(next) => toggleField('additional_fees', next)}
                                disabled={bulk.running}
                            >
                                <AdditionalFeesEditor
                                    fees={fees}
                                    onChange={setFees}
                                    feeTypes={feeTypes}
                                    feeDiscriminators={feeDiscriminators}
                                />
                            </FieldToggleBlock>
                        </div>
                    )}

                    {currentSection === 2 && (
                        <FieldToggleBlock enabled={enabled.price} onToggle={(next) => toggleField('price', next)} disabled={bulk.running}>
                            <div className="flex flex-col gap-4">
                                <div className="flex items-start gap-2 rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800 dark:bg-amber-950/30 dark:text-amber-300">
                                    <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
                                    <span>{t('properties.bulk-edit', 'price_overwrite_warning')}</span>
                                </div>

                                <SeasonTemplateGallery
                                    templates={templates}
                                    today={today}
                                    existingRanges={completeRanges.map((s) => ({
                                        start_date: s.start_date,
                                        end_date: s.end_date,
                                        name: s.name || undefined,
                                    }))}
                                    onPick={handlePickTemplate}
                                    disabled={bulk.running}
                                />

                                <div className="flex items-center justify-between">
                                    <Button onClick={handleAddSeason} variant={'outline'} className="border-secondary text-secondary border">
                                        <PlusIcon className="h-4 w-4" />
                                        {t('properties.bulk-edit', 'add_season')}
                                    </Button>

                                    <AlertDialog>
                                        <AlertDialogTrigger asChild>
                                            <Button
                                                variant={'outline'}
                                                disabled={bulk.running || seasons.length === 0}
                                                className="border border-red-300 text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30"
                                            >
                                                <Trash2Icon className="h-4 w-4" />
                                                {t('properties.bulk-edit', 'clean_all_seasons')}
                                            </Button>
                                        </AlertDialogTrigger>
                                        <AlertDialogContent>
                                            <AlertDialogHeader>
                                                <AlertDialogTitle>{t('properties.bulk-edit', 'clean_all_confirm_title')}</AlertDialogTitle>
                                                <AlertDialogDescription>{t('properties.bulk-edit', 'clean_all_confirm_message')}</AlertDialogDescription>
                                            </AlertDialogHeader>
                                            <AlertDialogFooter>
                                                <AlertDialogCancel>{t('properties.bulk-edit', 'clean_all_confirm_cancel')}</AlertDialogCancel>
                                                <AlertDialogAction
                                                    className="bg-red-600 hover:bg-red-700"
                                                    onClick={() => {
                                                        setSeasons([]);
                                                        setFocusPriceIndex(null);
                                                    }}
                                                >
                                                    {t('properties.bulk-edit', 'clean_all_confirm_action')}
                                                </AlertDialogAction>
                                            </AlertDialogFooter>
                                        </AlertDialogContent>
                                    </AlertDialog>
                                </div>

                                {seasons.map((season, index) => (
                                    <div
                                        key={index}
                                        className="flex flex-col items-start justify-between gap-4 rounded-md border p-4 md:flex-row"
                                    >
                                        <div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2">
                                            <SeasonFormFields
                                                value={season}
                                                onChange={(patch) => handleSeasonChange(index, patch)}
                                                autoFocusPrice={index === focusPriceIndex}
                                            />
                                            <div className="flex items-center gap-2">
                                                <Button variant={'outline'} className="cursor-pointer" onClick={() => removeSeason(index)}>
                                                    <Trash2Icon className="h-4 w-4 text-red-500" />
                                                </Button>
                                            </div>
                                        </div>
                                    </div>
                                ))}

                                <div className="flex flex-col gap-2 rounded-md border p-4">
                                    <label className="flex w-fit cursor-pointer items-center gap-2 text-sm font-medium">
                                        <Checkbox
                                            checked={baseEnabled}
                                            onCheckedChange={(v) => setBaseEnabled(v === true)}
                                            disabled={bulk.running}
                                        />
                                        {t('properties.step6', 'base_price_label')}
                                    </label>
                                    <Input
                                        type="number"
                                        min={0}
                                        className="max-w-xs"
                                        placeholder={t('properties.step6', 'base_price_placeholder')}
                                        value={basePrice}
                                        onChange={(e) => setBasePrice(e.target.value)}
                                        disabled={!baseEnabled || bulk.running}
                                    />
                                    <p className="text-muted-foreground text-xs">{t('properties.step6', 'base_price_help')}</p>
                                </div>

                                <SeasonTimeline today={today} horizon={horizon} segments={timelineSegments} />
                            </div>
                        </FieldToggleBlock>
                    )}

                    {currentSection === SELECTION_SECTION && (
                        <div className="flex flex-col gap-4">
                            {(bulk.running || bulk.finished) && (
                                <BulkProgressPanel
                                    running={bulk.running}
                                    processed={bulk.processed}
                                    total={bulk.total}
                                    succeeded={bulk.succeeded}
                                    failed={bulk.failed}
                                />
                            )}

                            <PropertyPicker
                                properties={properties}
                                selectedUids={selectedUids}
                                onChange={setSelectedUids}
                                disabled={bulk.running}
                            />

                            <Button
                                className="w-full"
                                disabled={!anyFieldEnabled || selectedUids.length === 0 || bulk.running}
                                onClick={handleApplyClick}
                            >
                                {bulk.running
                                    ? t('properties.bulk-edit', 'applying')
                                    : `${t('properties.bulk-edit', 'apply_to')} ${selectedUids.length} ${t('properties.bulk-edit', 'editing_count')}`}
                            </Button>
                        </div>
                    )}
                </div>
            </div>

            <AlertDialog open={confirmOpen} onOpenChange={setConfirmOpen}>
                <AlertDialogContent>
                    <AlertDialogHeader>
                        <AlertDialogTitle>{t('properties.bulk-edit', 'confirm_title')}</AlertDialogTitle>
                        <AlertDialogDescription>
                            {t('properties.bulk-edit', 'confirm_description')} {selectedUids.length}{' '}
                            {t('properties.bulk-edit', 'confirm_count_suffix')}
                        </AlertDialogDescription>
                    </AlertDialogHeader>
                    <div className="flex flex-col gap-1">
                        <p className="text-sm font-medium">{t('properties.bulk-edit', 'fields_to_apply')}</p>
                        <ul className="text-muted-foreground list-disc ps-5 text-sm">
                            {pendingJobs.map((job) => (
                                <li key={job.field}>
                                    {t('properties.bulk-edit', `field_${job.field}`)}
                                    {job.field === 'price' && Array.isArray(job.value.seasons)
                                        ? ` — ${job.value.seasons.length} ${t('properties.step6', 'ranges_count_suffix')}`
                                        : ''}
                                </li>
                            ))}
                        </ul>
                    </div>
                    <AlertDialogFooter>
                        <AlertDialogCancel>{t('properties.bulk-edit', 'cancel')}</AlertDialogCancel>
                        <AlertDialogAction onClick={handleConfirm}>{t('properties.bulk-edit', 'apply')}</AlertDialogAction>
                    </AlertDialogFooter>
                </AlertDialogContent>
            </AlertDialog>

            {bulkResolve && (
                <SeasonOverlapDialog
                    open
                    newSeason={bulkResolve.current}
                    conflict={bulkResolve.conflicts[bulkResolve.ci]}
                    index={bulkResolve.ci}
                    total={bulkResolve.conflicts.length}
                    onResolve={resolveBulkConflict}
                    onCancel={cancelBulkResolve}
                />
            )}
        </AppLayout>
    );
}
