"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import useEntries from "@/features/entries/hooks/use-entries";
import useSchedules from "@/features/entries/hooks/use-schedules";
import { useGroupsContext } from "@/hooks/groups-context";
import { useNotificationsContext } from "@/hooks/notifications-context";
import { useEntryMutation } from "@/hooks/entry-mutation-context";
import useTags from "@/features/tags/hooks/use-tags";
import useGroupSettings from "@/features/groups/hooks/use-group-settings";
import useUserSettings from "@/hooks/use-user-settings";
import ToggleButtonGroup from "@/components/toggle-button-group";
import NewEntryModal from "@/components/new-entry-modal";
import EntryDetailsModal from "@/components/entry-details-modal";
import NewScheduleModal, { type NewScheduleForm } from "@/components/new-schedule-modal";
import ScheduleDetailsModal, { type ScheduleDetailsForm } from "@/components/schedule-details-modal";
import EntriesList from "@/features/entries/components/entries-list";
import SchedulesList from "@/features/entries/components/schedules-list";
import EntriesFilterModal, { type EntriesFilters } from "@/features/entries/components/entries-filter-modal";
import ConfirmSlideModal from "@/components/confirm-slide-modal";

const EMPTY_FILTERS: EntriesFilters = {
    amountMin: "",
    amountMax: "",
    dateFrom: "",
    dateTo: "",
    necessity: "ANY",
    notesQuery: "",
    tags: [],
    tagsMode: "ANY"
};

function normalizeTagList(tags: string[]) {
    return tags.map(tag => tag.toLowerCase()).sort().join("|");
}

function isEditableTarget(target: EventTarget | null) {
    if (!(target instanceof HTMLElement)) return false;
    if (target.isContentEditable) return true;
    const tag = target.tagName;
    return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT";
}

function ListProgressSignal({
    hasMore,
    shownCount,
    totalCount,
    noun
}: {
    hasMore: boolean;
    shownCount: number;
    totalCount: number;
    noun: "entries" | "schedules";
}) {
    if (totalCount <= 0) return null;

    return (
        <div
            className={`mt-3 flex items-center justify-center gap-2 rounded-lg border px-3 py-2 text-xs ${hasMore ? "border-accent-weak bg-accent-soft text-[color:var(--color-text)]" : "border-accent-weak bg-panel text-soft"}`}
            aria-live="polite"
        >
            <span className={`inline-flex h-5 w-5 items-center justify-center rounded-full border ${hasMore ? "border-accent bg-panel text-[color:var(--color-accent)]" : "border-accent-weak text-soft"}`}>
                {hasMore ? "\u21e3" : "\u2713"}
            </span>
            <span>
                {hasMore
                    ? `Keep scrolling for more ${noun} (${shownCount} of ${totalCount})`
                    : `You have reached the end of ${noun} (${totalCount} total)`}
            </span>
        </div>
    );
}

export default function EntriesPanel() {
    const today = new Date().toISOString().slice(0, 10);
    const { groups, activeGroupId } = useGroupsContext();
    const { entries, loading: entriesLoading, error: entriesError, createEntry, updateEntry, deleteEntry } = useEntries(activeGroupId);
    const { schedules, loading: schedulesLoading, error: schedulesError, createSchedule, updateSchedule, deleteSchedule } = useSchedules(activeGroupId);
    const { settings: userSettings } = useUserSettings();
    const { notify } = useNotificationsContext();
    const { notifyEntryMutation } = useEntryMutation();
    const { tags: tagSuggestions } = useTags(activeGroupId);
    const { settings: groupSettings } = useGroupSettings(activeGroupId);
    const router = useRouter();

    const activeGroup = groups.find(group => group.id === activeGroupId) || null;
    const canManageTags = Boolean(activeGroup && (activeGroup.role !== "MEMBER" || groupSettings.allowMemberTagManage));
    const emptyTagActionLabel = canManageTags
        ? "No Tags Assigned Yet - Click To Assign Tags"
        : "No Tags Assigned Yet - Contact Your Group Admin";

    const pageSize = Math.max(1, Number(userSettings.entryPanelPageSize || 10));

    const [entryTab, setEntryTab] = useState<"ENTRIES" | "SCHEDULES">("ENTRIES");
    const [filters, setFilters] = useState<EntriesFilters>(EMPTY_FILTERS);
    const [entryVisibleCount, setEntryVisibleCount] = useState(pageSize);
    const [scheduleVisibleCount, setScheduleVisibleCount] = useState(pageSize);
    const [filterOpen, setFilterOpen] = useState(false);

    const [newEntryOpen, setNewEntryOpen] = useState(false);
    const [newScheduleOpen, setNewScheduleOpen] = useState(false);
    const [entryDetailsOpen, setEntryDetailsOpen] = useState(false);
    const [scheduleDetailsOpen, setScheduleDetailsOpen] = useState(false);
    const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);
    const [deleteTarget, setDeleteTarget] = useState<"ENTRY" | "SCHEDULE">("ENTRY");

    const [selectedEntryId, setSelectedEntryId] = useState<number | null>(null);
    const [selectedEntryIndex, setSelectedEntryIndex] = useState<number | null>(null);
    const [selectedScheduleId, setSelectedScheduleId] = useState<number | null>(null);

    const [entryRemovedTags, setEntryRemovedTags] = useState<string[]>([]);
    const [scheduleRemovedTags, setScheduleRemovedTags] = useState<string[]>([]);

    const [entryForm, setEntryForm] = useState({
        amountDollars: "",
        occurredAt: today,
        necessity: "NECESSARY",
        notes: "",
        tags: [] as string[],
        entryType: "SPENDING" as "SPENDING" | "INCOME"
    });
    const [entryDetailsForm, setEntryDetailsForm] = useState({
        amountDollars: "",
        occurredAt: today,
        necessity: "NECESSARY",
        notes: "",
        tags: [] as string[],
        entryType: "SPENDING" as "SPENDING" | "INCOME"
    });
    const [entryDetailsOriginal, setEntryDetailsOriginal] = useState<typeof entryDetailsForm | null>(null);

    const [scheduleForm, setScheduleForm] = useState<NewScheduleForm>({
        amountDollars: "",
        startsOn: today,
        necessity: "NECESSARY",
        notes: "",
        tags: [],
        entryType: "SPENDING",
        frequency: "MONTHLY",
        intervalCount: 1,
        endCondition: "NEVER",
        endCount: "",
        endDate: "",
        createEntryNow: false
    });
    const [scheduleDetailsForm, setScheduleDetailsForm] = useState<ScheduleDetailsForm>({
        amountDollars: "",
        startsOn: today,
        necessity: "NECESSARY",
        notes: "",
        tags: [],
        entryType: "SPENDING",
        frequency: "MONTHLY",
        intervalCount: 1,
        endCondition: "NEVER",
        endCount: "",
        endDate: "",
        nextRunOn: today,
        isActive: true
    });
    const [scheduleDetailsOriginal, setScheduleDetailsOriginal] = useState<ScheduleDetailsForm | null>(null);

    const amountInputRef = useRef<HTMLInputElement>(null);
    const tagsInputRef = useRef<HTMLInputElement>(null);
    const entriesLoadSentinelRef = useRef<HTMLDivElement>(null);
    const schedulesLoadSentinelRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        setEntryVisibleCount(pageSize);
        setScheduleVisibleCount(pageSize);
    }, [pageSize]);

    const activeFilterCount = useMemo(() => {
        let count = 0;
        if (filters.amountMin) count += 1;
        if (filters.amountMax) count += 1;
        if (filters.dateFrom) count += 1;
        if (filters.dateTo) count += 1;
        if (filters.necessity !== "ANY") count += 1;
        if (filters.notesQuery.trim()) count += 1;
        if (filters.tags.length) count += 1;
        return count;
    }, [filters]);

    const filteredEntries = useMemo(() => {
        const min = filters.amountMin ? Number(filters.amountMin) : null;
        const max = filters.amountMax ? Number(filters.amountMax) : null;
        const from = filters.dateFrom ? new Date(filters.dateFrom).getTime() : null;
        const to = filters.dateTo ? new Date(filters.dateTo).getTime() : null;
        const query = filters.notesQuery.trim().toLowerCase();
        const tagsFilter = filters.tags.map(tag => tag.toLowerCase());
        return entries.filter(entry => {
            if (min != null && entry.amountDollars < min) return false;
            if (max != null && entry.amountDollars > max) return false;
            const time = new Date(entry.occurredAt).getTime();
            if (from != null && !Number.isNaN(from) && time < from) return false;
            if (to != null && !Number.isNaN(to) && time > to + 24 * 60 * 60 * 1000 - 1) return false;
            if (filters.necessity !== "ANY" && entry.necessity !== filters.necessity) return false;
            if (query && !(entry.notes || "").toLowerCase().includes(query)) return false;
            if (tagsFilter.length) {
                const entryTags = (entry.tags || []).map(tag => tag.toLowerCase());
                if (filters.tagsMode === "ALL") {
                    if (!tagsFilter.every(tag => entryTags.includes(tag))) return false;
                } else if (!tagsFilter.some(tag => entryTags.includes(tag))) return false;
            }
            return true;
        });
    }, [entries, filters]);

    const filteredSchedules = useMemo(() => {
        const min = filters.amountMin ? Number(filters.amountMin) : null;
        const max = filters.amountMax ? Number(filters.amountMax) : null;
        const from = filters.dateFrom ? new Date(filters.dateFrom).getTime() : null;
        const to = filters.dateTo ? new Date(filters.dateTo).getTime() : null;
        const query = filters.notesQuery.trim().toLowerCase();
        const tagsFilter = filters.tags.map(tag => tag.toLowerCase());
        return schedules.filter(schedule => {
            if (min != null && schedule.amountDollars < min) return false;
            if (max != null && schedule.amountDollars > max) return false;
            const time = new Date(schedule.startsOn).getTime();
            if (from != null && !Number.isNaN(from) && time < from) return false;
            if (to != null && !Number.isNaN(to) && time > to + 24 * 60 * 60 * 1000 - 1) return false;
            if (filters.necessity !== "ANY" && schedule.necessity !== filters.necessity) return false;
            if (query && !(schedule.notes || "").toLowerCase().includes(query)) return false;
            if (tagsFilter.length) {
                const scheduleTags = (schedule.tags || []).map(tag => tag.toLowerCase());
                if (filters.tagsMode === "ALL") {
                    if (!tagsFilter.every(tag => scheduleTags.includes(tag))) return false;
                } else if (!tagsFilter.some(tag => scheduleTags.includes(tag))) return false;
            }
            return true;
        });
    }, [schedules, filters]);

    const visibleEntries = useMemo(() => filteredEntries.slice(0, entryVisibleCount), [filteredEntries, entryVisibleCount]);
    const visibleSchedules = useMemo(() => filteredSchedules.slice(0, scheduleVisibleCount), [filteredSchedules, scheduleVisibleCount]);
    const hasMoreEntries = filteredEntries.length > visibleEntries.length;
    const hasMoreSchedules = filteredSchedules.length > visibleSchedules.length;

    useEffect(() => {
        if (entryTab !== "ENTRIES" || !hasMoreEntries) return;

        let touchY: number | null = null;
        let lastLoadAt = 0;
        let lastScrollY = window.scrollY;

        function shouldLoadMore() {
            const sentinel = entriesLoadSentinelRef.current;
            if (!sentinel) return false;
            const rect = sentinel.getBoundingClientRect();
            return rect.top <= window.innerHeight + 48;
        }

        function tryLoadMore() {
            if (!shouldLoadMore()) return;
            const now = Date.now();
            if (now - lastLoadAt < 150) return;
            lastLoadAt = now;
            setEntryVisibleCount(prev => {
                if (prev >= filteredEntries.length) return prev;
                return Math.min(prev + pageSize, filteredEntries.length);
            });
        }

        function onWheel(event: WheelEvent) {
            if (event.deltaY <= 0) return;
            tryLoadMore();
        }

        function onScroll() {
            const nextY = window.scrollY;
            if (nextY <= lastScrollY) {
                lastScrollY = nextY;
                return;
            }
            lastScrollY = nextY;
            tryLoadMore();
        }

        function onKeyDown(event: KeyboardEvent) {
            if (isEditableTarget(event.target)) return;
            if (event.key === "ArrowDown" || event.key === "PageDown" || event.key === "End" || (event.key === " " && !event.shiftKey)) {
                tryLoadMore();
            }
        }

        function onTouchStart(event: TouchEvent) {
            touchY = event.touches[0]?.clientY ?? null;
        }

        function onTouchMove(event: TouchEvent) {
            const nextY = event.touches[0]?.clientY;
            if (touchY == null || nextY == null) return;
            const delta = touchY - nextY;
            touchY = nextY;
            if (delta > 10) tryLoadMore();
        }

        window.addEventListener("wheel", onWheel, { passive: true });
        window.addEventListener("scroll", onScroll, { passive: true });
        window.addEventListener("keydown", onKeyDown);
        window.addEventListener("touchstart", onTouchStart, { passive: true });
        window.addEventListener("touchmove", onTouchMove, { passive: true });

        return () => {
            window.removeEventListener("wheel", onWheel);
            window.removeEventListener("scroll", onScroll);
            window.removeEventListener("keydown", onKeyDown);
            window.removeEventListener("touchstart", onTouchStart);
            window.removeEventListener("touchmove", onTouchMove);
        };
    }, [entryTab, hasMoreEntries, filteredEntries.length, pageSize]);

    useEffect(() => {
        if (entryTab !== "SCHEDULES" || !hasMoreSchedules) return;

        let touchY: number | null = null;
        let lastLoadAt = 0;
        let lastScrollY = window.scrollY;

        function shouldLoadMore() {
            const sentinel = schedulesLoadSentinelRef.current;
            if (!sentinel) return false;
            const rect = sentinel.getBoundingClientRect();
            return rect.top <= window.innerHeight + 48;
        }

        function tryLoadMore() {
            if (!shouldLoadMore()) return;
            const now = Date.now();
            if (now - lastLoadAt < 150) return;
            lastLoadAt = now;
            setScheduleVisibleCount(prev => {
                if (prev >= filteredSchedules.length) return prev;
                return Math.min(prev + pageSize, filteredSchedules.length);
            });
        }

        function onWheel(event: WheelEvent) {
            if (event.deltaY <= 0) return;
            tryLoadMore();
        }

        function onScroll() {
            const nextY = window.scrollY;
            if (nextY <= lastScrollY) {
                lastScrollY = nextY;
                return;
            }
            lastScrollY = nextY;
            tryLoadMore();
        }

        function onKeyDown(event: KeyboardEvent) {
            if (isEditableTarget(event.target)) return;
            if (event.key === "ArrowDown" || event.key === "PageDown" || event.key === "End" || (event.key === " " && !event.shiftKey)) {
                tryLoadMore();
            }
        }

        function onTouchStart(event: TouchEvent) {
            touchY = event.touches[0]?.clientY ?? null;
        }

        function onTouchMove(event: TouchEvent) {
            const nextY = event.touches[0]?.clientY;
            if (touchY == null || nextY == null) return;
            const delta = touchY - nextY;
            touchY = nextY;
            if (delta > 10) tryLoadMore();
        }

        window.addEventListener("wheel", onWheel, { passive: true });
        window.addEventListener("scroll", onScroll, { passive: true });
        window.addEventListener("keydown", onKeyDown);
        window.addEventListener("touchstart", onTouchStart, { passive: true });
        window.addEventListener("touchmove", onTouchMove, { passive: true });

        return () => {
            window.removeEventListener("wheel", onWheel);
            window.removeEventListener("scroll", onScroll);
            window.removeEventListener("keydown", onKeyDown);
            window.removeEventListener("touchstart", onTouchStart);
            window.removeEventListener("touchmove", onTouchMove);
        };
    }, [entryTab, hasMoreSchedules, filteredSchedules.length, pageSize]);

    function clearFilters() {
        setFilters(EMPTY_FILTERS);
        setEntryVisibleCount(pageSize);
        setScheduleVisibleCount(pageSize);
    }

    function handleEmptyTagAction() {
        if (!canManageTags) return;
        router.push("/groups/settings");
    }

    function hasEntryChanges() {
        if (!entryDetailsOriginal) return false;
        const tags = entryDetailsForm.tags.filter(tag => !entryRemovedTags.includes(tag));
        return (
            entryDetailsForm.amountDollars !== entryDetailsOriginal.amountDollars ||
            entryDetailsForm.occurredAt !== entryDetailsOriginal.occurredAt ||
            entryDetailsForm.necessity !== entryDetailsOriginal.necessity ||
            entryDetailsForm.notes !== entryDetailsOriginal.notes ||
            entryDetailsForm.entryType !== entryDetailsOriginal.entryType ||
            normalizeTagList(tags) !== normalizeTagList(entryDetailsOriginal.tags)
        );
    }

    function hasScheduleChanges() {
        if (!scheduleDetailsOriginal) return false;
        const tags = scheduleDetailsForm.tags.filter(tag => !scheduleRemovedTags.includes(tag));
        return (
            scheduleDetailsForm.amountDollars !== scheduleDetailsOriginal.amountDollars ||
            scheduleDetailsForm.startsOn !== scheduleDetailsOriginal.startsOn ||
            scheduleDetailsForm.necessity !== scheduleDetailsOriginal.necessity ||
            scheduleDetailsForm.notes !== scheduleDetailsOriginal.notes ||
            scheduleDetailsForm.entryType !== scheduleDetailsOriginal.entryType ||
            scheduleDetailsForm.frequency !== scheduleDetailsOriginal.frequency ||
            scheduleDetailsForm.intervalCount !== scheduleDetailsOriginal.intervalCount ||
            scheduleDetailsForm.endCondition !== scheduleDetailsOriginal.endCondition ||
            scheduleDetailsForm.endCount !== scheduleDetailsOriginal.endCount ||
            scheduleDetailsForm.endDate !== scheduleDetailsOriginal.endDate ||
            scheduleDetailsForm.nextRunOn !== scheduleDetailsOriginal.nextRunOn ||
            scheduleDetailsForm.isActive !== scheduleDetailsOriginal.isActive ||
            normalizeTagList(tags) !== normalizeTagList(scheduleDetailsOriginal.tags)
        );
    }

    async function submitNewEntry(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        if (!e.currentTarget.reportValidity()) return;
        const amountDollars = Number(entryForm.amountDollars || 0);
        if (!Number.isFinite(amountDollars) || amountDollars <= 0 || !entryForm.tags.length) return;
        const created = await createEntry({
            entryType: entryForm.entryType,
            amountDollars,
            occurredAt: entryForm.occurredAt,
            necessity: entryForm.necessity as "NECESSARY" | "BOTH" | "UNNECESSARY",
            purchaseType: entryForm.tags.join(", ") || "General",
            notes: entryForm.notes.trim() || undefined,
            tags: entryForm.tags
        });
        if (!created) return;
        setNewEntryOpen(false);
        setEntryForm({ amountDollars: "", occurredAt: today, necessity: "NECESSARY", notes: "", tags: [], entryType: "SPENDING" });
        notify({ title: "Entry added", message: `${created.tags.join(", ")} - $${created.amountDollars.toFixed(2)}`, tone: "success" });
        notifyEntryMutation();
    }

    async function submitNewSchedule(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        const amountDollars = Number(scheduleForm.amountDollars || 0);
        if (!Number.isFinite(amountDollars) || amountDollars <= 0 || !scheduleForm.tags.length) return;
        const created = await createSchedule({
            entryType: scheduleForm.entryType,
            amountDollars,
            startsOn: scheduleForm.startsOn,
            necessity: scheduleForm.necessity,
            purchaseType: scheduleForm.tags.join(", ") || "General",
            notes: scheduleForm.notes.trim() || undefined,
            tags: scheduleForm.tags,
            frequency: scheduleForm.frequency,
            intervalCount: scheduleForm.intervalCount,
            endCondition: scheduleForm.endCondition,
            endCount: scheduleForm.endCondition === "AFTER_COUNT" ? Number(scheduleForm.endCount || 0) || null : null,
            endDate: scheduleForm.endCondition === "BY_DATE" ? scheduleForm.endDate || null : null,
            createEntryNow: scheduleForm.createEntryNow
        });
        if (!created) return;
        setNewScheduleOpen(false);
        setScheduleForm({ amountDollars: "", startsOn: today, necessity: "NECESSARY", notes: "", tags: [], entryType: "SPENDING", frequency: "MONTHLY", intervalCount: 1, endCondition: "NEVER", endCount: "", endDate: "", createEntryNow: false });
        notify({ title: "Schedule added", message: `${created.tags.join(", ") || "Schedule"} - $${created.amountDollars.toFixed(2)}`, tone: "success" });
        if (scheduleForm.createEntryNow) notifyEntryMutation();
    }

    function openEntryDetails(id: number) {
        const index = filteredEntries.findIndex(entry => Number(entry.id) === Number(id));
        if (index < 0) return;
        const entry = filteredEntries[index];
        const form = { amountDollars: String(entry.amountDollars), occurredAt: entry.occurredAt, necessity: entry.necessity, notes: entry.notes || "", tags: entry.tags || [], entryType: entry.entryType };
        setSelectedEntryId(Number(id));
        setSelectedEntryIndex(index);
        setEntryDetailsForm(form);
        setEntryDetailsOriginal(form);
        setEntryRemovedTags([]);
        setEntryDetailsOpen(true);
    }

    function openScheduleDetails(id: number) {
        const schedule = schedules.find(item => Number(item.id) === Number(id));
        if (!schedule) return;
        const form: ScheduleDetailsForm = {
            amountDollars: String(schedule.amountDollars),
            startsOn: schedule.startsOn,
            necessity: schedule.necessity,
            notes: schedule.notes || "",
            tags: schedule.tags || [],
            entryType: schedule.entryType,
            frequency: schedule.frequency,
            intervalCount: schedule.intervalCount,
            endCondition: schedule.endCondition,
            endCount: schedule.endCount == null ? "" : String(schedule.endCount),
            endDate: schedule.endDate || "",
            nextRunOn: schedule.nextRunOn,
            isActive: schedule.isActive
        };
        setSelectedScheduleId(Number(id));
        setScheduleDetailsForm(form);
        setScheduleDetailsOriginal(form);
        setScheduleRemovedTags([]);
        setScheduleDetailsOpen(true);
    }

    async function submitEntryUpdate(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        if (!selectedEntryId || !hasEntryChanges()) return;
        const amount = Number(entryDetailsForm.amountDollars || 0);
        const tags = entryDetailsForm.tags.filter(tag => !entryRemovedTags.includes(tag));
        if (!Number.isFinite(amount) || amount <= 0 || !tags.length) return;
        const updated = await updateEntry({
            id: selectedEntryId,
            entryType: entryDetailsForm.entryType,
            amountDollars: amount,
            occurredAt: entryDetailsForm.occurredAt,
            necessity: entryDetailsForm.necessity as "NECESSARY" | "BOTH" | "UNNECESSARY",
            purchaseType: tags.join(", ") || "General",
            notes: entryDetailsForm.notes.trim() || undefined,
            tags
        });
        if (!updated) return;
        setEntryDetailsOpen(false);
        notify({ title: "Entry updated", message: `${updated.tags.join(", ")} - $${updated.amountDollars.toFixed(2)}` });
        notifyEntryMutation();
    }

    async function submitScheduleUpdate(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        if (!selectedScheduleId || !hasScheduleChanges()) return;
        const amount = Number(scheduleDetailsForm.amountDollars || 0);
        const tags = scheduleDetailsForm.tags.filter(tag => !scheduleRemovedTags.includes(tag));
        if (!Number.isFinite(amount) || amount <= 0 || !tags.length) return;
        const updated = await updateSchedule({
            id: selectedScheduleId,
            entryType: scheduleDetailsForm.entryType,
            amountDollars: amount,
            startsOn: scheduleDetailsForm.startsOn,
            necessity: scheduleDetailsForm.necessity,
            purchaseType: tags.join(", ") || "General",
            notes: scheduleDetailsForm.notes.trim() || undefined,
            tags,
            frequency: scheduleDetailsForm.frequency,
            intervalCount: scheduleDetailsForm.intervalCount,
            endCondition: scheduleDetailsForm.endCondition,
            endCount: scheduleDetailsForm.endCondition === "AFTER_COUNT" ? Number(scheduleDetailsForm.endCount || 0) || null : null,
            endDate: scheduleDetailsForm.endCondition === "BY_DATE" ? scheduleDetailsForm.endDate || null : null,
            nextRunOn: scheduleDetailsForm.nextRunOn,
            isActive: scheduleDetailsForm.isActive
        });
        if (!updated) return;
        setScheduleDetailsOpen(false);
        notify({ title: "Schedule updated", message: `${updated.tags.join(", ")} - $${updated.amountDollars.toFixed(2)}` });
    }

    async function confirmDelete() {
        if (deleteTarget === "ENTRY" && selectedEntryId) {
            const removed = await deleteEntry(selectedEntryId);
            if (!removed) return;
            setEntryDetailsOpen(false);
            notify({ title: "Entry deleted", message: removed.tags.join(", ") || "Entry removed", tone: "danger" });
            notifyEntryMutation();
        }
        if (deleteTarget === "SCHEDULE" && selectedScheduleId) {
            const removed = await deleteSchedule(selectedScheduleId);
            if (!removed) return;
            setScheduleDetailsOpen(false);
            notify({ title: "Schedule deleted", message: removed.tags.join(", ") || "Schedule removed", tone: "danger" });
        }
    }

    function prevEntry() {
        if (!filteredEntries.length) return;
        const current = selectedEntryIndex ?? 0;
        const index = current === 0 ? filteredEntries.length - 1 : current - 1;
        openEntryDetails(filteredEntries[index].id);
    }

    function nextEntry() {
        if (!filteredEntries.length) return;
        const current = selectedEntryIndex ?? 0;
        const index = current === filteredEntries.length - 1 ? 0 : current + 1;
        openEntryDetails(filteredEntries[index].id);
    }

    return (
        <>
            <div className="space-y-4">
                <div className="panel panel-accent p-4">
                    <div className="card-header">
                        <ToggleButtonGroup
                            value={entryTab}
                            onChange={setEntryTab}
                            ariaLabel="Entries and schedules tab"
                            sizeClassName="px-3 py-2 text-xs font-semibold"
                            options={[
                                { value: "ENTRIES", label: "Entries", className: "mr-[-10px] w-24" },
                                { value: "SCHEDULES", label: "Schedules", className: "w-24" }
                            ]}
                        />
                        <div className="flex items-center gap-2">
                            <button type="button" onClick={() => setFilterOpen(true)} className="rounded-lg btn-outline-accent px-3 py-1 text-xs font-semibold disabled:opacity-50" disabled={!activeGroupId}>
                                Filters{activeFilterCount ? ` (${activeFilterCount})` : ""}
                            </button>
                            <button type="button" onClick={() => entryTab === "ENTRIES" ? setNewEntryOpen(true) : setNewScheduleOpen(true)} className="flex h-8 w-8 -translate-y-0.5 items-center justify-center rounded-full border border-accent bg-panel text-lg font-semibold leading-none hover:border-accent-strong disabled:opacity-50" disabled={!activeGroupId} aria-label={entryTab === "ENTRIES" ? "Add entry" : "Add schedule"}>
                                +
                            </button>
                        </div>
                    </div>
                    {entryTab === "ENTRIES" ? (
                        <>
                            <EntriesList
                                activeGroupId={activeGroupId}
                                loading={entriesLoading}
                                entries={entries}
                                visibleEntries={visibleEntries}
                                activeFilterCount={activeFilterCount}
                                onOpenDetails={entry => openEntryDetails(entry.id)}
                                onClearFilters={clearFilters}
                            />
                            <div ref={entriesLoadSentinelRef} className="h-1" aria-hidden="true" />
                            <ListProgressSignal
                                hasMore={hasMoreEntries}
                                shownCount={visibleEntries.length}
                                totalCount={filteredEntries.length}
                                noun="entries"
                            />
                        </>
                    ) : (
                        <>
                            <SchedulesList
                                activeGroupId={activeGroupId}
                                loading={schedulesLoading}
                                schedules={schedules}
                                visibleSchedules={visibleSchedules}
                                activeFilterCount={activeFilterCount}
                                onOpenDetails={schedule => openScheduleDetails(schedule.id)}
                                onClearFilters={clearFilters}
                            />
                            <div ref={schedulesLoadSentinelRef} className="h-1" aria-hidden="true" />
                            <ListProgressSignal
                                hasMore={hasMoreSchedules}
                                shownCount={visibleSchedules.length}
                                totalCount={filteredSchedules.length}
                                noun="schedules"
                            />
                        </>
                    )}
                </div>
            </div>
            <NewEntryModal
                isOpen={newEntryOpen && Boolean(activeGroupId)}
                form={entryForm}
                error={entriesError}
                onClose={() => setNewEntryOpen(false)}
                onSubmit={submitNewEntry}
                onChange={next => setEntryForm(prev => ({ ...prev, ...next }))}
                tagSuggestions={tagSuggestions}
                emptyTagActionLabel={emptyTagActionLabel}
                emptyTagActionDisabled={!canManageTags}
                onEmptyTagAction={handleEmptyTagAction}
                amountInputRef={amountInputRef}
                tagsInputRef={tagsInputRef}
            />
            <NewScheduleModal
                isOpen={newScheduleOpen && Boolean(activeGroupId)}
                form={scheduleForm}
                error={schedulesError}
                onClose={() => setNewScheduleOpen(false)}
                onSubmit={submitNewSchedule}
                onChange={next => setScheduleForm(prev => ({ ...prev, ...next }))}
                tagSuggestions={tagSuggestions}
                emptyTagActionLabel={emptyTagActionLabel}
                emptyTagActionDisabled={!canManageTags}
                onEmptyTagAction={handleEmptyTagAction}
            />
            <EntryDetailsModal
                isOpen={entryDetailsOpen}
                form={entryDetailsForm}
                originalForm={entryDetailsOriginal}
                isDirty={hasEntryChanges()}
                error={entriesError}
                onClose={() => setEntryDetailsOpen(false)}
                onSubmit={submitEntryUpdate}
                onRequestDelete={() => {
                    setDeleteTarget("ENTRY");
                    setConfirmDeleteOpen(true);
                }}
                onRevert={() => {
                    if (!entryDetailsOriginal) return;
                    setEntryDetailsForm(entryDetailsOriginal);
                    setEntryRemovedTags([]);
                }}
                onChange={next => setEntryDetailsForm(prev => ({ ...prev, ...next }))}
                onAddTag={tag => {
                    setEntryDetailsForm(prev => ({ ...prev, tags: prev.tags.includes(tag) ? prev.tags : [...prev.tags, tag] }));
                    setEntryRemovedTags(prev => prev.filter(item => item !== tag));
                }}
                onToggleTag={tag => setEntryRemovedTags(prev => prev.includes(tag) ? prev.filter(item => item !== tag) : [...prev, tag])}
                removedTags={entryRemovedTags}
                tagSuggestions={tagSuggestions}
                emptyTagActionLabel={emptyTagActionLabel}
                emptyTagActionDisabled={!canManageTags}
                onEmptyTagAction={handleEmptyTagAction}
                onPrev={prevEntry}
                onNext={nextEntry}
                loopHintPrev={selectedEntryIndex === 0 && filteredEntries.length > 1 ? "Loop" : ""}
                loopHintNext={selectedEntryIndex === filteredEntries.length - 1 && filteredEntries.length > 1 ? "Loop" : ""}
                canNavigate={filteredEntries.length > 1}
            />
            <ScheduleDetailsModal
                isOpen={scheduleDetailsOpen}
                form={scheduleDetailsForm}
                originalForm={scheduleDetailsOriginal}
                isDirty={hasScheduleChanges()}
                error={schedulesError}
                onClose={() => setScheduleDetailsOpen(false)}
                onSubmit={submitScheduleUpdate}
                onRequestDelete={() => {
                    setDeleteTarget("SCHEDULE");
                    setConfirmDeleteOpen(true);
                }}
                onRevert={() => {
                    if (!scheduleDetailsOriginal) return;
                    setScheduleDetailsForm(scheduleDetailsOriginal);
                    setScheduleRemovedTags([]);
                }}
                onChange={next => setScheduleDetailsForm(prev => ({ ...prev, ...next }))}
                onAddTag={tag => {
                    setScheduleDetailsForm(prev => ({ ...prev, tags: prev.tags.includes(tag) ? prev.tags : [...prev.tags, tag] }));
                    setScheduleRemovedTags(prev => prev.filter(item => item !== tag));
                }}
                onToggleTag={tag => setScheduleRemovedTags(prev => prev.includes(tag) ? prev.filter(item => item !== tag) : [...prev, tag])}
                removedTags={scheduleRemovedTags}
                tagSuggestions={tagSuggestions}
                emptyTagActionLabel={emptyTagActionLabel}
                emptyTagActionDisabled={!canManageTags}
                onEmptyTagAction={handleEmptyTagAction}
            />
            <EntriesFilterModal
                isOpen={filterOpen}
                filters={filters}
                setFilters={setFilters}
                activeFilterCount={activeFilterCount}
                tagSuggestions={tagSuggestions}
                canManageTags={canManageTags}
                emptyTagActionLabel={emptyTagActionLabel}
                onEmptyTagAction={handleEmptyTagAction}
                onClearFilters={clearFilters}
                onFilterAddTag={tag => setFilters(prev => (prev.tags.includes(tag) ? prev : { ...prev, tags: [...prev.tags, tag] }))}
                onFilterToggleTag={tag => setFilters(prev => ({ ...prev, tags: prev.tags.filter(item => item !== tag) }))}
                onClose={() => setFilterOpen(false)}
            />
            <ConfirmSlideModal
                isOpen={confirmDeleteOpen}
                title={deleteTarget === "ENTRY" ? "Delete entry" : "Delete schedule"}
                description={deleteTarget === "ENTRY" ? "This will permanently remove the entry and its tags." : "This will permanently remove the schedule and its tags."}
                confirmLabel={deleteTarget === "ENTRY" ? "Delete entry" : "Delete schedule"}
                onClose={() => setConfirmDeleteOpen(false)}
                onConfirm={() => {
                    setConfirmDeleteOpen(false);
                    confirmDelete();
                }}
            />
        </>
    );
}
