import { createSignal, createMemo, For, createEffect, JSXElement } from "solid-js";
import { Icon } from "./Icons.tsx";

const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
const MONTHS = [
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
];

// -- Shared Tailwind class constants (also used by DatePicker) --
export const CAL_PICKER_ROOT = "p-2 min-w-[240px]";
export const CAL_MONTH_ROOT = "p-0 min-w-0 w-full bg-white border border-neutral-200 rounded-default shadow-sm overflow-hidden";

export const CAL_HEADER_PICKER = "flex items-center justify-between mb-2 gap-1";
export const CAL_HEADER_MONTH = "flex items-center justify-between gap-1 py-3 px-4 border-b border-neutral-200 bg-neutral-50";

export const CAL_NAV_BTN = "bg-transparent border-0 p-1 cursor-pointer text-text-muted rounded-sm flex items-center justify-center hover:bg-neutral-100 hover:text-text-body";

export const CAL_MY_PICKER = "text-sm font-semibold text-text-heading mx-3 whitespace-nowrap";
export const CAL_MY_MONTH = "text-lg font-heading mx-4 flex-1 text-center font-semibold text-text-heading whitespace-nowrap";

export const CAL_WEEKDAYS_PICKER = "grid grid-cols-7 gap-[2px] mb-1";
export const CAL_WEEKDAYS_MONTH = "grid grid-cols-7 border-b border-neutral-200";
export const CAL_WEEKDAY_PICKER = "text-center text-xs font-semibold text-text-muted p-1";
export const CAL_WEEKDAY_MONTH = "text-center text-xs font-semibold text-text-muted p-2 uppercase tracking-wider";

export const CAL_DAYS_PICKER = "grid grid-cols-7 gap-[2px]";
export const CAL_DAYS_MONTH = "grid grid-cols-7";

export const CAL_DAY_PICKER_BASE = "aspect-square flex items-center justify-center text-sm bg-transparent border-0 rounded-sm cursor-pointer text-text-body p-0";
export const CAL_DAY_MONTH_BASE = "min-h-[6.5rem] flex flex-col items-stretch justify-start p-1.5 border-r border-b border-neutral-200 text-left gap-1 text-xs bg-transparent cursor-pointer";

export const CAL_SELECT = "flex-1 py-1 px-2 text-sm font-semibold border border-neutral-200 rounded-sm bg-white text-text-heading cursor-pointer focus:outline-hidden focus:border-primary";

function getDaysInMonth(year: number, month: number): number {
    return new Date(year, month + 1, 0).getDate();
}

function getFirstDayOfMonth(year: number, month: number): number {
    return new Date(year, month, 1).getDay();
}

function toDateKey(date: Date | null | undefined): string {
    if (!date) return "";
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, "0");
    const d = String(date.getDate()).padStart(2, "0");
    return `${y}-${m}-${d}`;
}

export type CalendarVariant = "picker" | "month";

interface CalendarProps {
    selected?: string | Date;
    viewMonth?: string | Date;
    onSelect?: (key: string) => void;
    variant?: CalendarVariant;
    renderDay?: (key: string, date: Date) => JSXElement;
    _reset?: unknown;
}

export function Calendar(props: CalendarProps) {
    const today = new Date();
    const [viewMonth, setViewMonth] = createSignal(new Date(today.getFullYear(), today.getMonth(), 1));
    const [key, setKey] = createSignal(0);

    createEffect(() => {
        if (props.selected) {
            const d = new Date(props.selected);
            if (!isNaN(d.getTime())) {
                setViewMonth(new Date(d.getFullYear(), d.getMonth(), 1));
            }
        }
    });

    createEffect(() => {
        props._reset;
        setKey(k => k + 1);
        setViewMonth(new Date(today.getFullYear(), today.getMonth(), 1));
    });

    // Sync viewMonth from parent only when the parent's value actually changes
    // to a different month. Tracking a stamp prevents the parent from clobbering
    // the user's local month navigation on unrelated re-renders.
    let lastPropStamp: number | null = null;
    createEffect(() => {
        const vm = props.viewMonth;
        if (!vm) return;
        const d = vm instanceof Date ? vm : new Date(vm);
        if (!(d instanceof Date) || isNaN(d.getTime())) return;
        const stamp = d.getFullYear() * 12 + d.getMonth();
        if (stamp === lastPropStamp) return;
        lastPropStamp = stamp;
        setViewMonth(new Date(d.getFullYear(), d.getMonth(), 1));
    });

    const currentMonth = createMemo(() => viewMonth().getMonth());
    const currentYear = createMemo(() => viewMonth().getFullYear());

    const days = createMemo(() => {
        const year = currentYear();
        const month = currentMonth();
        const daysInMonth = getDaysInMonth(year, month);
        const firstDay = getFirstDayOfMonth(year, month);

        const daysArray: (Date | null)[] = [];
        for (let i = 0; i < firstDay; i++) {
            daysArray.push(null);
        }
        for (let i = 1; i <= daysInMonth; i++) {
            daysArray.push(new Date(year, month, i));
        }
        return daysArray;
    });

    const prevMonth = () => setViewMonth(new Date(currentYear(), currentMonth() - 1, 1));
    const nextMonth = () => setViewMonth(new Date(currentYear(), currentMonth() + 1, 1));

    const isSelected = (date: Date | null): boolean => {
        if (!date || !props.selected) return false;
        const sel = new Date(props.selected);
        if (isNaN(sel.getTime())) return false;
        return date.getFullYear() === sel.getFullYear() &&
               date.getMonth() === sel.getMonth() &&
               date.getDate() === sel.getDate();
    };

    const isToday = (date: Date | null): boolean => {
        if (!date) return false;
        return date.getFullYear() === today.getFullYear() &&
               date.getMonth() === today.getMonth() &&
               date.getDate() === today.getDate();
    };

    const selectDate = (date: Date | null) => {
        if (!date) return;
        props.onSelect?.(toDateKey(date));
    };

    const variant = (): CalendarVariant => props.variant || "picker";
    const isMonth = () => variant() === "month";

    const rootCls = () => isMonth() ? CAL_MONTH_ROOT : CAL_PICKER_ROOT;
    const headerCls = () => isMonth() ? CAL_HEADER_MONTH : CAL_HEADER_PICKER;
    const myCls = () => isMonth() ? CAL_MY_MONTH : CAL_MY_PICKER;
    const weekdaysCls = () => isMonth() ? CAL_WEEKDAYS_MONTH : CAL_WEEKDAYS_PICKER;
    const weekdayCls = () => isMonth() ? CAL_WEEKDAY_MONTH : CAL_WEEKDAY_PICKER;
    const daysCls = () => isMonth() ? CAL_DAYS_MONTH : CAL_DAYS_PICKER;

    // Day button class. nth-child(7n) (last column) skips right border in
    // month variant — we compute it from the array index since the Tailwind
    // compiler doesn't support [&:nth-child(7n)] arbitrary variants.
    const dayClass = (date: Date | null, idx: number): string => {
        const empty = !date;
        const selected = isSelected(date);
        const today = isToday(date);

        if (isMonth()) {
            let c = CAL_DAY_MONTH_BASE;
            if (idx % 7 === 6) c += " border-r-0";
            if (empty) c += " bg-neutral-50 cursor-default";
            else c += " hover:bg-neutral-50";
            if (selected) c += " bg-primary/10 text-text-body";
            return c;
        }
        let c = CAL_DAY_PICKER_BASE;
        if (empty) c += " cursor-default";
        else c += " hover:bg-neutral-100";
        if (today) c += " font-bold text-primary";
        if (selected) c += " !bg-primary !text-white";
        return c;
    };

    const dayNumberClass = (date: Date | null): string => {
        if (isMonth()) {
            const base = "text-sm font-semibold text-text-muted self-end px-1 py-0.5";
            if (date && isToday(date)) {
                return "bg-primary text-white rounded-full w-6 h-6 inline-flex items-center justify-center p-0 self-end text-sm font-semibold";
            }
            return base;
        }
        return "leading-none";
    };

    

    return (
        <div class={rootCls()} attr:key={key()}>
            <div class={headerCls()}>
                <button class={CAL_NAV_BTN} onclick={prevMonth}>
                    <Icon icon="chevron-left" size={16}/>
                </button>
                <span class={myCls()}>{MONTHS[currentMonth()] + " " + currentYear()}</span>
                <button class={CAL_NAV_BTN} onclick={nextMonth}>
                    <Icon icon="chevron-right" size={16}/>
                </button>
            </div>
            <div class={weekdaysCls()}>
                <For each={DAYS}>{(day) => <div class={weekdayCls()}>{day}</div>}</For>
            </div>
            <div class={daysCls()}>
                <For each={days()}>{(date, idx) => (
                    <button class={dayClass(date, idx())} onclick={() => selectDate(date)} disabled={!date}>
                        <span class={dayNumberClass(date)}>{date ? date.getDate() : ""}</span>
                        {date && props.renderDay ? props.renderDay(toDateKey(date), date) : ""}
                    </button>
                )}</For>
            </div>
        </div>
    );
}
