import { createContext, useContext, createSignal, createEffect, onCleanup, For, Show, JSXElement } from "solid-js";
import { Icon } from "./Icons.tsx";

export type ToastType = "success" | "error" | "warning" | "info" | "generic";
export type ToastPosition = "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center";

export interface ToastConfig {
    message: string;
    type?: ToastType;
    duration?: number | null;
    dismissible?: boolean;
    showProgress?: boolean;
}

interface ToastInstance extends ToastConfig {
    id: string;
}

interface ToastContextValue {
    addToast: (config: ToastConfig) => string;
    removeToast: (id: string) => void;
    success: (message: string, options?: Partial<ToastConfig>) => string;
    error: (message: string, options?: Partial<ToastConfig>) => string;
    warning: (message: string, options?: Partial<ToastConfig>) => string;
    info: (message: string, options?: Partial<ToastConfig>) => string;
    generic: (message: string, options?: Partial<ToastConfig>) => string;
}

interface ToastProviderProps {
    position?: ToastPosition;
    maxToasts?: number;
    children?: JSXElement;
}

const ToastContext = createContext<ToastContextValue | null>(null);

export function useToast(): ToastContextValue {
    const context = useContext(ToastContext);
    if (!context) {
        throw new Error("useToast must be used within a ToastProvider");
    }
    return context;
}

const TOAST_TYPE_ICONS: Record<ToastType, string | null> = {
    success: "circle-check",
    error: "circle-xmark",
    warning: "triangle-exclamation",
    info: "circle-info",
    generic: null,
};

const CONTAINER_BASE = "fixed z-[200] flex flex-col gap-2";
const CONTAINER_POSITIONS: Record<ToastPosition, string> = {
    "top-right":     "top-4 right-4",
    "top-left":      "top-4 left-4",
    "bottom-right":  "bottom-4 right-4 flex-col-reverse",
    "bottom-left":   "bottom-4 left-4 flex-col-reverse",
    "top-center":    "top-4 left-1/2 -translate-x-1/2",
    "bottom-center": "bottom-4 left-1/2 -translate-x-1/2 flex-col-reverse",
};

const TOAST_BASE = "relative overflow-hidden rounded-default shadow-lg border border-neutral-200 border-l-4 bg-white min-w-72 max-w-md transition-[opacity,transform] duration-150 ease-out";
const TOAST_TYPE_BORDER: Record<ToastType, string> = {
    success: "border-l-green-700",
    error:   "border-l-red-700",
    warning: "border-l-yellow-500",
    info:    "border-l-sky-800",
    generic: "border-l-neutral-400",
};
const TOAST_ICON_COLOR: Record<ToastType, string> = {
    success: "text-green-600",
    error:   "text-red-600",
    warning: "text-yellow-600",
    info:    "text-sky-700",
    generic: "",
};

const DEFAULT_DURATION = 5000;

let toastCounter = 0;
function generateId(): string {
    return "toast-" + (++toastCounter) + "-" + Date.now();
}

interface ToastItemProps {
    toast: ToastInstance;
    onDismiss: (id: string) => void;
}

function ToastItem(props: ToastItemProps) {
    const type = () => props.toast.type ?? "info";
    const duration = () => props.toast.duration ?? DEFAULT_DURATION;
    const dismissible = () => props.toast.dismissible !== false;
    const showProgress = () => props.toast.showProgress !== false;

    let timeoutRef: ReturnType<typeof setTimeout> | null = null;
    let animationFrameRef: number | null = null;
    let startTime = Date.now();

    const [isExiting, setIsExiting] = createSignal(false);
    const [progress, setProgress] = createSignal(100);

    const handleDismiss = () => {
        setIsExiting(true);
        setTimeout(() => props.onDismiss(props.toast.id), 150);
    };

    createEffect(() => {
        const dur = duration();
        if (dur !== null && dur > 0) {
            timeoutRef = setTimeout(handleDismiss, dur);
            startTime = Date.now();

            const updateProgress = () => {
                const elapsed = Date.now() - startTime;
                const remaining = Math.max(0, 100 - (elapsed / dur) * 100);
                setProgress(remaining);
                if (remaining > 0) {
                    animationFrameRef = requestAnimationFrame(updateProgress);
                }
            };
            animationFrameRef = requestAnimationFrame(updateProgress);
        }

        onCleanup(() => {
            if (timeoutRef) {
                clearTimeout(timeoutRef);
            }
            if (animationFrameRef) {
                cancelAnimationFrame(animationFrameRef);
            }
        });
    });

    const icon = () => TOAST_TYPE_ICONS[type()];
    const shouldShowProgress = () => showProgress() && duration() !== null && duration()! > 0;

    const toastClass = () => {
        let c = TOAST_BASE + " " + TOAST_TYPE_BORDER[type()];
        if (isExiting()) c += " opacity-0 translate-x-2";
        return c;
    };

    return (
        <div class={toastClass()} role="alert">
            <div class="flex items-start gap-3 p-4">
                <Show when={icon()}>
                    <Icon icon={icon()} size={20} class={"shrink-0 mt-0.5 " + TOAST_ICON_COLOR[type()]}/>
                </Show>
                <div class="flex-1 text-sm text-neutral-800">{props.toast.message}</div>
                <Show when={dismissible()}>
                    <button onclick={handleDismiss} class="shrink-0 cursor-pointer text-neutral-400 hover:text-neutral-600 bg-transparent border-0 p-0 transition-colors" aria-label="Dismiss">
                        <Icon icon="xmark" size={16}/>
                    </button>
                </Show>
            </div>
            <Show when={shouldShowProgress()}>
                <div class="h-1 w-full bg-neutral-100">
                    <div class="h-full bg-neutral-300" style={{ width: progress() + "%" }}/>
                </div>
            </Show>
        </div>
    );
}

export function ToastProvider(props: ToastProviderProps) {
    const [toasts, setToasts] = createSignal<ToastInstance[]>([]);

    const position = () => props.position ?? "bottom-right";
    const maxToasts = () => props.maxToasts ?? 5;

    const removeToast = (id: string) => {
        setToasts((prev) => prev.filter((t) => t.id !== id));
    };

    const addToast = (config: ToastConfig): string => {
        const id = generateId();
        const newToast: ToastInstance = { ...config, id };
        setToasts((prev) => {
            const updated = [...prev, newToast];
            if (updated.length > maxToasts()) {
                return updated.slice(-maxToasts());
            }
            return updated;
        });
        return id;
    };

    const success = (message: string, options?: Partial<ToastConfig>) => addToast({ message, type: "success", ...options });
    const error = (message: string, options?: Partial<ToastConfig>) => addToast({ message, type: "error", ...options });
    const warning = (message: string, options?: Partial<ToastConfig>) => addToast({ message, type: "warning", ...options });
    const info = (message: string, options?: Partial<ToastConfig>) => addToast({ message, type: "info", ...options });
    const generic = (message: string, options?: Partial<ToastConfig>) => addToast({ message, type: "generic", ...options });

    const value: ToastContextValue = {
        addToast,
        removeToast,
        success,
        error,
        warning,
        info,
        generic,
    };

    return (
        <ToastContext.Provider value={value}>
            {props.children}
            <div class={CONTAINER_BASE + " " + CONTAINER_POSITIONS[position()]} aria-live="polite" aria-label="Notifications">
                <For each={toasts()}>{(toast) => (
                    <ToastItem toast={toast} onDismiss={removeToast}/>
                )}</For>
            </div>
        </ToastContext.Provider>
    );
}
