import AccessTimeOutlined from "@mui/icons-material/AccessTimeOutlined";
import ChevronLeftOutlined from "@mui/icons-material/ChevronLeftOutlined";
import ChevronRightOutlined from "@mui/icons-material/ChevronRightOutlined";
import KeyboardArrowDownOutlined from "@mui/icons-material/KeyboardArrowDownOutlined";
import IconButton from "@mui/material/IconButton";
import MenuItem from "@mui/material/MenuItem";
import Popover from "@mui/material/Popover";
import TextField from "@mui/material/TextField";
import { useEffect, useMemo, useState } from "react";
import { Button } from "@/shared/ui/Button.jsx";
import styles from "@/shared/ui/TimeRangeFilter.module.css";

const emptyRange = { endMs: "", startMs: "" };
const quickRanges = [
    ["quickHour", 60 * 60 * 1000],
    ["quickDay", 24 * 60 * 60 * 1000],
    ["quickWeek", 7 * 24 * 60 * 60 * 1000],
];
const defaultMessages = {
    apply: "确定",
    clear: "清空",
    completeRange: "选择完整区间",
    duration: "间隔 {value}",
    durationDays: "{count} 天",
    durationHours: "{count} 小时",
    durationMinutes: "{count} 分钟",
    end: "结束时间",
    endAfterStart: "结束时间必须晚于开始时间",
    endDate: "结束日期",
    endDateAfterStart: "结束日期不能早于开始日期",
    endDateLaterShort: "结束不能早于开始",
    endLaterShort: "结束需晚于开始",
    endMark: "末",
    hour: "时",
    lessMinute: "间隔少于 1 分钟",
    minute: "分",
    nextMonth: "下个月",
    pending: "待选择",
    previousMonth: "上个月",
    quickDay: "近 24 小时",
    quickHour: "近 1 小时",
    quickWeek: "近 7 天",
    sameDay: "同一天",
    start: "开始时间",
    startDate: "开始日期",
    startMark: "始",
    unlimited: "不限",
};

// withTime=false 时是纯日期区间选择器：隐藏时/分与小时级快捷区间，
// 选中的开始/结束日分别取当天 00:00:00.000 与 23:59:59.999 的本地毫秒。
export function TimeRangeFilter({ className = "", direction = "ltr", disabled = false, label = "时间", locale = "zh-CN", messages, onChange, value, withTime = true }) {
    const copy = useMemo(() => ({ ...defaultMessages, ...messages }), [messages]);
    const range = useMemo(() => normalizeRange(value), [value]);
    const [anchorEl, setAnchorEl] = useState(null);
    const [draft, setDraft] = useState(() => range);
    const [activeField, setActiveField] = useState("start");
    const [viewDate, setViewDate] = useState(() => dateForField(range, "start", new Date()));
    const [error, setError] = useState("");
    const open = Boolean(anchorEl);
    const days = useMemo(() => calendarDays(viewDate), [viewDate]);
    // 结束时间尚未选择时沿用开始时间作为时分基准，避免自动切换到结束字段后，
    // 时分下拉意外跳回当前月 1 日的 00:00。
    const selectedDate = dateForField(
        draft,
        activeField,
        activeField === "end" && draft.startMs ? new Date(draft.startMs) : viewDate,
    );
    const rangeDistance = formatLocalizedRangeDistance(draft.startMs, draft.endMs, withTime, copy);
    const weekLabels = useMemo(() => localizedWeekLabels(locale), [locale]);
    const hasRange = Boolean(range.startMs || range.endMs);
    // CSS Modules do not pass through the external-admin Emotion RTL cache. Keep
    // popover anchoring and directional glyphs explicit so Arabic mirrors as a unit.
    const rtl = direction === "rtl";
    const horizontalOrigin = rtl ? "right" : "left";
    const PreviousMonthIcon = rtl ? ChevronRightOutlined : ChevronLeftOutlined;
    const NextMonthIcon = rtl ? ChevronLeftOutlined : ChevronRightOutlined;

    useEffect(() => {
        if (!open) {
            setDraft(range);
            setActiveField("start");
            setViewDate(dateForField(range, "start", new Date()));
            setError("");
        }
    }, [open, range]);

    const formatPart = withTime ? formatRangePart : formatDayPart;
    const displayLabel = useMemo(
        () =>
            !range.startMs && !range.endMs
                ? label
                : `${formatPart(range.startMs) || copy.unlimited} - ${formatPart(range.endMs) || copy.unlimited}`,
        [copy.unlimited, formatPart, label, range],
    );

    const openPopover = (event) => {
        if (disabled) {
            return;
        }
        setDraft(range);
        setActiveField("start");
        setViewDate(dateForField(range, "start", new Date()));
        setAnchorEl(event.currentTarget);
    };
    const closePopover = () => setAnchorEl(null);
    const applyRange = (nextRange) => {
        onChange(normalizeRange(nextRange));
        setAnchorEl(null);
        setError("");
    };
    const applyQuickRange = (durationMs) => {
        const endMs = new Date().getTime();
        applyRange({ endMs, startMs: endMs - durationMs });
    };
    const activateField = (field) => {
        setActiveField(field);
        setViewDate(
            dateForField(draft, field, field === "end" && draft.startMs ? new Date(draft.startMs) : viewDate),
        );
        setError("");
    };
    const changeMonth = (offset) => {
        setViewDate((current) => new Date(current.getFullYear(), current.getMonth() + offset, 1));
    };
    const changeDay = (day) => {
        const editingField = activeField;
        setDraft((current) => {
            const base = dateForField(current, editingField, viewDate);
            const timeParts = withTime
                ? [base.getHours(), base.getMinutes(), 0, 0]
                : editingField === "start"
                  ? [0, 0, 0, 0]
                  : [23, 59, 59, 999];
            const nextDate = new Date(day.getFullYear(), day.getMonth(), day.getDate(), ...timeParts);
            return setRangeField(current, editingField, nextDate.getTime());
        });
        setViewDate(new Date(day.getFullYear(), day.getMonth(), 1));
        setError("");
        if (editingField === "start") {
            // 日期区间遵循“先起点、后终点”的连续操作：开始日期落点后直接进入结束日期选择，
            // 用户无需再回到顶部手动点击结束字段。
            setActiveField("end");
        }
    };
    const changeTime = (unit, nextValue) => {
        setDraft((current) => {
            const nextDate = new Date(dateForField(current, activeField, viewDate));
            if (unit === "hour") {
                nextDate.setHours(Number(nextValue));
            } else {
                nextDate.setMinutes(Number(nextValue));
            }
            nextDate.setSeconds(0, 0);
            return setRangeField(current, activeField, nextDate.getTime());
        });
    };
    const clearRange = () => applyRange(emptyRange);
    const submitDraft = (event) => {
        event.preventDefault();
        // Popover content is rendered through a portal, but React still bubbles
        // submit events through the component tree. Stop here so a picker confirm
        // never submits an outer edit drawer before the new range reaches state.
        event.stopPropagation();
        const nextRange = normalizeRange(draft);
        if (nextRange.startMs && nextRange.endMs && nextRange.endMs <= nextRange.startMs) {
            setError(withTime ? copy.endAfterStart : copy.endDateAfterStart);
            return;
        }
        applyRange(nextRange);
    };

    return (
        <>
            <Button
                className={[styles.trigger, className].filter(Boolean).join(" ")}
                aria-expanded={open}
                aria-haspopup="dialog"
                disabled={disabled}
                onClick={openPopover}
            >
                <span className={styles.triggerMeta}>
                    <AccessTimeOutlined fontSize="inherit" />
                    <span>{label}</span>
                </span>
                <span className={styles.triggerText}>{hasRange ? displayLabel : copy.unlimited}</span>
                <KeyboardArrowDownOutlined className={styles.triggerChevron} fontSize="small" />
            </Button>
            <Popover
                anchorEl={anchorEl}
                open={open}
                anchorOrigin={{ horizontal: horizontalOrigin, vertical: "bottom" }}
                slotProps={{ paper: { className: styles.paper, dir: direction } }}
                transformOrigin={{ horizontal: horizontalOrigin, vertical: "top" }}
                onClose={closePopover}
            >
                <form className={styles.panel} onSubmit={submitDraft}>
                    {withTime ? (
                        <div className={styles.quickRanges}>
                            {quickRanges.map(([quickKey, durationMs]) => (
                                <Button
                                    key={quickKey}
                                    className={styles.quickButton}
                                    onClick={() => applyQuickRange(durationMs)}
                                >
                                    {copy[quickKey]}
                                </Button>
                            ))}
                        </div>
                    ) : null}
                    <div className={styles.rangeFields}>
                        <RangeFieldButton
                            active={activeField === "start"}
                            formatPart={formatPart}
                            label={withTime ? copy.start : copy.startDate}
                            unlimited={copy.unlimited}
                            value={draft.startMs}
                            onClick={() => activateField("start")}
                        />
                        <RangeFieldButton
                            active={activeField === "end"}
                            formatPart={formatPart}
                            label={withTime ? copy.end : copy.endDate}
                            unlimited={copy.unlimited}
                            value={draft.endMs}
                            onClick={() => activateField("end")}
                        />
                    </div>
                    <div className={styles.rangeOverview} aria-live="polite">
                        <span className={styles.rangeEdge}>
                            <span className={`${styles.rangeEdgeMarker} ${styles.rangeEdgeMarkerStart}`}>{copy.startMark}</span>
                            {formatDayPosition(draft.startMs) || copy.pending}
                        </span>
                        <span className={styles.rangeDistance}>{rangeDistance}</span>
                        <span className={`${styles.rangeEdge} ${styles.rangeEdgeEnd}`}>
                            {formatDayPosition(draft.endMs) || copy.pending}
                            <span className={`${styles.rangeEdgeMarker} ${styles.rangeEdgeMarkerEnd}`}>{copy.endMark}</span>
                        </span>
                    </div>
                    <div className={styles.calendarHeader}>
                        <IconButton aria-label={copy.previousMonth} size="small" onClick={() => changeMonth(-1)}>
                            <PreviousMonthIcon fontSize="small" />
                        </IconButton>
                        <span className={styles.calendarTitle}>{monthTitle(viewDate, locale)}</span>
                        <IconButton aria-label={copy.nextMonth} size="small" onClick={() => changeMonth(1)}>
                            <NextMonthIcon fontSize="small" />
                        </IconButton>
                    </div>
                    <div className={`${styles.calendarGrid} ${styles.rangeCalendarGrid}`}>
                        {weekLabels.map((weekday) => (
                            <span className={styles.weekday} key={weekday}>
                                {weekday}
                            </span>
                        ))}
                        {days.map((day) => {
                            const isOutside = day.getMonth() !== viewDate.getMonth();
                            const isRangeStart = sameDayMs(day, draft.startMs);
                            const isRangeEnd = sameDayMs(day, draft.endMs);
                            const isInRange = isDayInRange(day, draft.startMs, draft.endMs);
                            const rangeEdge =
                                isRangeStart && isRangeEnd ? "始末" : isRangeStart ? "始" : isRangeEnd ? "末" : "";
                            return (
                                <button
                                    className={[
                                        styles.dayButton,
                                        isOutside ? styles.dayOutside : "",
                                        isInRange ? styles.dayInRange : "",
                                        isRangeStart ? styles.dayRangeStart : "",
                                        isRangeEnd ? styles.dayRangeEnd : "",
                                        isRangeStart || isRangeEnd ? styles.daySelected : "",
                                    ]
                                        .filter(Boolean)
                                        .join(" ")}
                                    data-range-edge={rangeEdge || undefined}
                                    key={`${day.getFullYear()}-${day.getMonth()}-${day.getDate()}`}
                                    type="button"
                                    onClick={() => changeDay(day)}
                                >
                                    {day.getDate()}
                                </button>
                            );
                        })}
                    </div>
                    {withTime ? (
                        <div className={styles.timeGrid}>
                            <TextField
                                label={copy.hour}
                                select
                                size="small"
                                value={String(selectedDate.getHours())}
                                onChange={(event) => changeTime("hour", event.target.value)}
                            >
                                {Array.from({ length: 24 }, (_, hour) => (
                                    <MenuItem key={hour} value={String(hour)}>
                                        {pad2(hour)}
                                    </MenuItem>
                                ))}
                            </TextField>
                            <TextField
                                label={copy.minute}
                                select
                                size="small"
                                value={String(selectedDate.getMinutes())}
                                onChange={(event) => changeTime("minute", event.target.value)}
                            >
                                {Array.from({ length: 60 }, (_, minute) => (
                                    <MenuItem key={minute} value={String(minute)}>
                                        {pad2(minute)}
                                    </MenuItem>
                                ))}
                            </TextField>
                        </div>
                    ) : null}
                    {error ? <div className={styles.error}>{error}</div> : null}
                    <div className={styles.actions}>
                        <Button onClick={clearRange}>{copy.clear}</Button>
                        <Button type="submit" variant="primary">
                            {copy.apply}
                        </Button>
                    </div>
                </form>
            </Popover>
        </>
    );
}

function RangeFieldButton({ active, formatPart = formatRangePart, label, onClick, unlimited = "不限", value }) {
    return (
        <button
            className={[styles.rangeField, active ? styles.rangeFieldActive : ""].filter(Boolean).join(" ")}
            type="button"
            onClick={onClick}
        >
            <span className={styles.rangeFieldLabel}>{label}</span>
            <span className={styles.rangeFieldValue}>{formatPart(value) || unlimited}</span>
        </button>
    );
}

export function normalizeRange(value) {
    return {
        endMs: normalizeMs(value?.endMs),
        startMs: normalizeMs(value?.startMs),
    };
}

export function timeRangeLabel(label, value) {
    const range = normalizeRange(value);
    if (!range.startMs && !range.endMs) {
        return label;
    }
    return `${formatRangePart(range.startMs) || "不限"} - ${formatRangePart(range.endMs) || "不限"}`;
}

export function datetimeTextToMs(value) {
    const trimmed = String(value || "").trim();
    if (!trimmed) {
        return "";
    }
    const match = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?$/.exec(trimmed);
    if (!match) {
        return null;
    }
    const [, year, month, day, hour, minute, second = "00"] = match;
    const date = new Date(
        Number(year),
        Number(month) - 1,
        Number(day),
        Number(hour),
        Number(minute),
        Number(second),
        0,
    );
    if (
        date.getFullYear() !== Number(year) ||
        date.getMonth() !== Number(month) - 1 ||
        date.getDate() !== Number(day) ||
        date.getHours() !== Number(hour) ||
        date.getMinutes() !== Number(minute) ||
        date.getSeconds() !== Number(second)
    ) {
        return null;
    }
    return date.getTime();
}

function dateForField(range, field, fallbackDate) {
    const ms = field === "start" ? range.startMs : range.endMs;
    return ms ? new Date(ms) : fallbackDate;
}

function setRangeField(range, field, value) {
    return field === "start" ? { ...range, startMs: value } : { ...range, endMs: value };
}

export function calendarDays(viewDate) {
    const firstDay = new Date(viewDate.getFullYear(), viewDate.getMonth(), 1);
    const mondayIndex = (firstDay.getDay() + 6) % 7;
    const start = new Date(firstDay);
    start.setDate(firstDay.getDate() - mondayIndex);

    return Array.from({ length: 42 }, (_, index) => {
        const day = new Date(start);
        day.setDate(start.getDate() + index);
        return day;
    });
}

export function sameDay(left, right) {
    return (
        left.getFullYear() === right.getFullYear() &&
        left.getMonth() === right.getMonth() &&
        left.getDate() === right.getDate()
    );
}

export function monthTitle(date, locale = "zh-CN") {
    if (locale === "zh-CN") {
        return `${date.getFullYear()}年${pad2(date.getMonth() + 1)}月`;
    }
    return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(date);
}

export function formatRangePart(value) {
    const text = msToDatetimeText(value);
    return text ? text.replace("-", "/").replace("-", "/") : "";
}

function formatDayPart(value) {
    const ms = normalizeMs(value);
    if (!ms) {
        return "";
    }
    const date = new Date(ms);
    return `${date.getFullYear()}/${pad2(date.getMonth() + 1)}/${pad2(date.getDate())}`;
}

function formatDayPosition(value) {
    const ms = normalizeMs(value);
    if (!ms) {
        return "";
    }
    const date = new Date(ms);
    return `${pad2(date.getMonth() + 1)}/${pad2(date.getDate())}`;
}

export function formatRangeDistance(startValue, endValue, withTime = true) {
    const startMs = normalizeMs(startValue);
    const endMs = normalizeMs(endValue);
    if (!startMs || !endMs) {
        return "选择完整区间";
    }
    if (endMs <= startMs) {
        return withTime ? "结束需晚于开始" : "结束不能早于开始";
    }

    if (!withTime) {
        const start = new Date(startMs);
        const end = new Date(endMs);
        // 使用 UTC 日期序号计算自然日间隔，避免跨夏令时日期出现 23/25 小时导致天数偏差。
        const startDay = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
        const endDay = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
        const days = Math.round((endDay - startDay) / (24 * 60 * 60 * 1000));
        return days === 0 ? "同一天" : `间隔 ${days} 天`;
    }

    const totalMinutes = Math.floor((endMs - startMs) / (60 * 1000));
    if (totalMinutes < 1) {
        return "间隔少于 1 分钟";
    }
    const days = Math.floor(totalMinutes / (24 * 60));
    const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
    const minutes = totalMinutes % 60;
    const parts = [
        days ? `${days} 天` : "",
        hours ? `${hours} 小时` : "",
        minutes ? `${minutes} 分钟` : "",
    ].filter(Boolean);
    return `间隔 ${parts.join(" ")}`;
}

function formatLocalizedRangeDistance(startValue, endValue, withTime, messages) {
    const startMs = normalizeMs(startValue);
    const endMs = normalizeMs(endValue);
    if (!startMs || !endMs) {
        return messages.completeRange;
    }
    if (endMs <= startMs) {
        return withTime ? messages.endLaterShort : messages.endDateLaterShort;
    }
    if (!withTime) {
        const start = new Date(startMs);
        const end = new Date(endMs);
        const startDay = Date.UTC(start.getFullYear(), start.getMonth(), start.getDate());
        const endDay = Date.UTC(end.getFullYear(), end.getMonth(), end.getDate());
        const days = Math.round((endDay - startDay) / (24 * 60 * 60 * 1000));
        return days === 0 ? messages.sameDay : template(messages.duration, { value: template(messages.durationDays, { count: days }) });
    }
    const totalMinutes = Math.floor((endMs - startMs) / (60 * 1000));
    if (totalMinutes < 1) {
        return messages.lessMinute;
    }
    const days = Math.floor(totalMinutes / (24 * 60));
    const hours = Math.floor((totalMinutes % (24 * 60)) / 60);
    const minutes = totalMinutes % 60;
    const parts = [
        days ? template(messages.durationDays, { count: days }) : "",
        hours ? template(messages.durationHours, { count: hours }) : "",
        minutes ? template(messages.durationMinutes, { count: minutes }) : "",
    ].filter(Boolean);
    return template(messages.duration, { value: parts.join(" ") });
}

function localizedWeekLabels(locale) {
    const formatter = new Intl.DateTimeFormat(locale, { weekday: "narrow" });
    return Array.from({ length: 7 }, (_, index) => formatter.format(new Date(2024, 0, 1 + index)));
}

function template(message, values) {
    return String(message).replace(/\{(\w+)\}/g, (match, key) => (values[key] === undefined ? match : String(values[key])));
}

function sameDayMs(day, value) {
    const ms = normalizeMs(value);
    return Boolean(ms) && sameDay(day, new Date(ms));
}

function isDayInRange(day, startValue, endValue) {
    const startMs = normalizeMs(startValue);
    const endMs = normalizeMs(endValue);
    if (!startMs || !endMs || endMs <= startMs) {
        return false;
    }
    const dayValue = new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
    const start = new Date(startMs);
    const end = new Date(endMs);
    const startDay = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();
    const endDay = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime();
    return dayValue >= startDay && dayValue <= endDay;
}

function msToDatetimeText(value) {
    const ms = normalizeMs(value);
    if (!ms) {
        return "";
    }
    const date = new Date(ms);
    return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
}

export function normalizeMs(value) {
    if (value === "" || value === null || value === undefined) {
        return "";
    }
    const number = Number(value);
    return Number.isFinite(number) && number > 0 ? number : "";
}

export function pad2(value) {
    return String(value).padStart(2, "0");
}
