import React, { useState, useCallback, useRef, useEffect } from 'react';
import * as ReactDOM from 'react-dom';
import { createPortal } from 'react-dom';
import { themeColors } from './colorExtractor';
import { colors } from './colors';

export type FlashType = 'success' | 'error' | 'warning' | 'info';

export interface FlashMessage {
    id: string;
    key?: string;
    type: FlashType;
    title?: string;
    message: string;
    duration?: number;
}

interface FlashStore {
    addFlash: (flash: Omit<FlashMessage, 'id'> & { id?: string }) => void;
    addError: (payload: { message: string; key?: string; title?: string }) => void;
    clearFlashes: (key?: string) => void;
    clearAndAddHttpError: (payload: { error?: Error | any | null; key?: string }) => void;
}

interface KeyedFlashStore {
    addError: (message: string, title?: string) => void;
    addFlash: (flash: Omit<FlashMessage, 'id' | 'key'>) => void;
    clearFlashes: () => void;
    clearAndAddHttpError: (error?: Error | string | null) => void;
}

const DEFAULT_DURATION = 5000;
const ANIMATION_DURATION = 300;

const httpErrorToHuman = (error: any): string => {
    if (!error) return 'An unexpected error occurred.';
    if (typeof error === 'string') return error;
    if (error.response?.data?.errors?.[0]?.detail) {
        return error.response.data.errors[0].detail;
    }
    if (error.response?.data?.message) {
        return error.response.data.message;
    }
    if (error.message) {
        return error.message;
    }
    return 'An unexpected error occurred.';
};

let globalFlashes: FlashMessage[] = [];
let globalExitingIds: Set<string> = new Set();
let globalTimers: Map<string, NodeJS.Timeout> = new Map();
let globalListeners: Set<() => void> = new Set();
let containerMounted = false;

const notifyListeners = () => {
    globalListeners.forEach((listener) => listener());
};

const dismissFlash = (id: string) => {
    globalExitingIds = new Set(globalExitingIds).add(id);
    notifyListeners();

    setTimeout(() => {
        globalFlashes = globalFlashes.filter((f) => f.id !== id);
        globalExitingIds = new Set(globalExitingIds);
        globalExitingIds.delete(id);
        notifyListeners();
    }, ANIMATION_DURATION);

    const timer = globalTimers.get(id);
    if (timer) {
        clearTimeout(timer);
        globalTimers.delete(id);
    }
};

const addFlashGlobal = (flash: Omit<FlashMessage, 'id'> & { id?: string }) => {
    const id = flash.id || `flash-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
    const duration = flash.duration ?? DEFAULT_DURATION;

    const newFlash: FlashMessage = {
        ...flash,
        id,
        duration,
    };

    globalFlashes = [...globalFlashes, newFlash];
    notifyListeners();

    if (duration > 0) {
        const timer = setTimeout(() => {
            dismissFlash(id);
        }, duration);
        globalTimers.set(id, timer);
    }
};

const clearFlashesGlobal = (key?: string) => {
    if (key) {
        const toRemove = globalFlashes.filter((f) => f.key === key);
        toRemove.forEach((f) => {
            const timer = globalTimers.get(f.id);
            if (timer) {
                clearTimeout(timer);
                globalTimers.delete(f.id);
            }
        });
        globalFlashes = globalFlashes.filter((f) => f.key !== key);
    } else {
        globalTimers.forEach((timer) => clearTimeout(timer));
        globalTimers.clear();
        globalFlashes = [];
    }
    notifyListeners();
};

const getTypeStyles = (type: FlashType) => {
    switch (type) {
        case 'success':
            return {
                bg: colors.success.opacity(0.15),
                border: colors.success.opacity(0.4),
                icon: colors.success.default,
                title: colors.success.light,
            };
        case 'error':
            return {
                bg: colors.danger.opacity(0.15),
                border: colors.danger.opacity(0.4),
                icon: colors.danger.default,
                title: colors.danger.light,
            };
        case 'warning':
            return {
                bg: colors.warning.opacity(0.15),
                border: colors.warning.opacity(0.4),
                icon: colors.warning.default,
                title: colors.warning.light,
            };
        case 'info':
        default:
            return {
                bg: colors.primary.opacity(0.15),
                border: colors.primary.opacity(0.4),
                icon: colors.primary.default,
                title: colors.primary.light,
            };
    }
};

const getIcon = (type: FlashType) => {
    switch (type) {
        case 'success':
            return (
                <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
                </svg>
            );
        case 'error':
            return (
                <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                </svg>
            );
        case 'warning':
            return (
                <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                </svg>
            );
        case 'info':
        default:
            return (
                <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                </svg>
            );
    }
};

interface FlashItemProps {
    flash: FlashMessage;
    onDismiss: (id: string) => void;
    isExiting: boolean;
}

const FlashItem: React.FC<FlashItemProps> = ({ flash, onDismiss, isExiting }) => {
    const styles = getTypeStyles(flash.type);
    const progressRef = useRef<HTMLDivElement>(null);
    const duration = flash.duration ?? DEFAULT_DURATION;

    useEffect(() => {
        if (progressRef.current && duration > 0) {
            progressRef.current.style.transition = `width ${duration}ms linear`;
            requestAnimationFrame(() => {
                if (progressRef.current) {
                    progressRef.current.style.width = '0%';
                }
            });
        }
    }, [duration]);

    return (
        <div
            className="relative overflow-hidden shadow-lg backdrop-blur-sm"
            style={{
                backgroundColor: styles.bg,
                border: `1px solid ${styles.border}`,
                borderRadius: themeColors.borderRadius.component,
                opacity: isExiting ? 0 : 1,
                transform: isExiting ? 'translateX(100%)' : 'translateX(0)',
                transition: `opacity ${ANIMATION_DURATION}ms ease-out, transform ${ANIMATION_DURATION}ms ease-out`,
                maxWidth: '400px',
                minWidth: '300px',
            }}
        >
            <div className="flex items-start gap-3 p-4">
                <div className="flex-shrink-0 mt-0.5" style={{ color: styles.icon }}>
                    {getIcon(flash.type)}
                </div>
                <div className="flex-1 min-w-0">
                    {flash.title && (
                        <p className="text-sm font-semibold mb-1" style={{ color: styles.title }}>
                            {flash.title}
                        </p>
                    )}
                    <p className="text-sm leading-relaxed" style={{ color: themeColors.page.primary }}>
                        {flash.message}
                    </p>
                </div>
                <button
                    onClick={() => onDismiss(flash.id)}
                    className="flex-shrink-0 p-1 rounded-md transition-colors hover:bg-white/10"
                    style={{ color: themeColors.page.primaryHover }}
                >
                    <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                    </svg>
                </button>
            </div>
            {duration > 0 && (
                <div
                    ref={progressRef}
                    className="absolute bottom-0 left-0 h-0.5"
                    style={{
                        width: '100%',
                        backgroundColor: styles.icon,
                        opacity: 0.6,
                    }}
                />
            )}
        </div>
    );
};

const FlashContainer: React.FC = () => {
    const [, forceUpdate] = useState({});

    useEffect(() => {
        const listener = () => forceUpdate({});
        globalListeners.add(listener);
        return () => {
            globalListeners.delete(listener);
        };
    }, []);

    if (typeof window === 'undefined' || globalFlashes.length === 0) return null;

    return createPortal(
        <div
            className="fixed top-4 right-4 z-[9999] flex flex-col gap-3 pointer-events-none"
            style={{ maxHeight: 'calc(100vh - 2rem)' }}
        >
            {globalFlashes.map((flash) => (
                <div key={flash.id} className="pointer-events-auto">
                    <FlashItem
                        flash={flash}
                        onDismiss={dismissFlash}
                        isExiting={globalExitingIds.has(flash.id)}
                    />
                </div>
            ))}
        </div>,
        document.body
    );
};

const FlashContainerMount: React.FC = () => {
    useEffect(() => {
        containerMounted = true;
        return () => {
            containerMounted = false;
        };
    }, []);

    return <FlashContainer />;
};

let containerElement: HTMLDivElement | null = null;
let containerRoot: any = null;

const ensureContainer = () => {
    if (typeof window === 'undefined') return;
    if (containerElement) return;

    containerElement = document.createElement('div');
    containerElement.id = 'better-flash-container';
    document.body.appendChild(containerElement);

    if ((ReactDOM as any).createRoot) {
        containerRoot = (ReactDOM as any).createRoot(containerElement);
        containerRoot.render(<FlashContainerMount />);
    } else {
        (ReactDOM as any).render(<FlashContainerMount />, containerElement);
    }
};

const useFlash = (): FlashStore => {
    useEffect(() => {
        ensureContainer();
    }, []);

    const addFlash = useCallback((flash: Omit<FlashMessage, 'id'> & { id?: string }) => {
        ensureContainer();
        addFlashGlobal(flash);
    }, []);

    const addError = useCallback((payload: { message: string; key?: string; title?: string }) => {
        addFlash({
            type: 'error',
            title: payload.title || 'Error',
            message: payload.message,
            key: payload.key,
        });
    }, [addFlash]);

    const clearFlashes = useCallback((key?: string) => {
        clearFlashesGlobal(key);
    }, []);

    const clearAndAddHttpError = useCallback((payload: { error?: Error | any | null; key?: string }) => {
        if (payload.key) {
            clearFlashesGlobal(payload.key);
        }
        if (payload.error) {
            addFlash({
                type: 'error',
                title: 'Error',
                message: httpErrorToHuman(payload.error),
                key: payload.key,
            });
        }
    }, [addFlash]);

    return {
        addFlash,
        addError,
        clearFlashes,
        clearAndAddHttpError,
    };
};

export const useFlashKey = (key: string): KeyedFlashStore => {
    const { addFlash, clearFlashes, clearAndAddHttpError } = useFlash();

    return {
        addError: (message, title) => addFlash({ key, message, title, type: 'error' }),
        addFlash: (flash) => addFlash({ ...flash, key }),
        clearFlashes: () => clearFlashes(key),
        clearAndAddHttpError: (error) => clearAndAddHttpError({ key, error }),
    };
};

export default useFlash;
