'use client';

import { isNodeSelection, type Editor } from '@tiptap/react';
import * as React from 'react';

// --- Hooks ---
import { useTiptapEditor } from '@/hooks/use-tiptap-editor';

// --- Icons ---
import { BoldIcon } from '@/components/tiptap-icons/bold-icon';
import { Code2Icon } from '@/components/tiptap-icons/code2-icon';
import { ItalicIcon } from '@/components/tiptap-icons/italic-icon';
import { StrikeIcon } from '@/components/tiptap-icons/strike-icon';
import { SubscriptIcon } from '@/components/tiptap-icons/subscript-icon';
import { SuperscriptIcon } from '@/components/tiptap-icons/superscript-icon';
import { UnderlineIcon } from '@/components/tiptap-icons/underline-icon';

// --- Lib ---
import { isMarkInSchema } from '@/lib/tiptap-utils';

// --- UI Primitives ---
import type { ButtonProps } from '@/components/tiptap-ui-primitive/button';
import { Button } from '@/components/tiptap-ui-primitive/button';

export type Mark = 'bold' | 'italic' | 'strike' | 'code' | 'underline' | 'superscript' | 'subscript';

export interface MarkButtonProps extends Omit<ButtonProps, 'type'> {
    /**
     * The type of mark to toggle
     */
    type: Mark;
    /**
     * Optional editor instance. If not provided, will use editor from context
     */
    editor?: Editor | null;
    /**
     * Display text for the button (optional)
     */
    text?: string;
    /**
     * Whether this button should be hidden when the mark is not available
     */
    hideWhenUnavailable?: boolean;
}

export const markIcons = {
    bold: BoldIcon,
    italic: ItalicIcon,
    underline: UnderlineIcon,
    strike: StrikeIcon,
    code: Code2Icon,
    superscript: SuperscriptIcon,
    subscript: SubscriptIcon,
};

export const markShortcutKeys: Partial<Record<Mark, string>> = {
    bold: 'Ctrl-b',
    italic: 'Ctrl-i',
    underline: 'Ctrl-u',
    strike: 'Ctrl-Shift-s',
    code: 'Ctrl-e',
    superscript: 'Ctrl-.',
    subscript: 'Ctrl-,',
};

export function canToggleMark(editor: Editor | null, type: Mark): boolean {
    if (!editor) return false;

    try {
        return editor.can().toggleMark(type);
    } catch {
        return false;
    }
}

export function isMarkActive(editor: Editor | null, type: Mark): boolean {
    if (!editor) return false;
    return editor.isActive(type);
}

export function toggleMark(editor: Editor | null, type: Mark): void {
    if (!editor) return;
    editor.chain().focus().toggleMark(type).run();
}

export function isMarkButtonDisabled(editor: Editor | null, type: Mark, userDisabled: boolean = false): boolean {
    if (!editor) return true;
    if (userDisabled) return true;
    if (editor.isActive('codeBlock')) return true;
    if (!canToggleMark(editor, type)) return true;
    return false;
}

export function shouldShowMarkButton(params: { editor: Editor | null; type: Mark; hideWhenUnavailable: boolean; markInSchema: boolean }): boolean {
    const { editor, type, hideWhenUnavailable, markInSchema } = params;

    if (!markInSchema || !editor) {
        return false;
    }

    if (hideWhenUnavailable) {
        if (isNodeSelection(editor.state.selection) || !canToggleMark(editor, type)) {
            return false;
        }
    }

    return true;
}

export function getFormattedMarkName(type: Mark): string {
    return type.charAt(0).toUpperCase() + type.slice(1);
}

export function useMarkState(editor: Editor | null, type: Mark, disabled: boolean = false) {
    const markInSchema = isMarkInSchema(type, editor);
    const isDisabled = isMarkButtonDisabled(editor, type, disabled);
    const isActive = isMarkActive(editor, type);

    const Icon = markIcons[type];
    const shortcutKey = markShortcutKeys[type];
    const formattedName = getFormattedMarkName(type);

    return {
        markInSchema,
        isDisabled,
        isActive,
        Icon,
        shortcutKey,
        formattedName,
    };
}

export const MarkButton = React.forwardRef<HTMLButtonElement, MarkButtonProps>(
    ({ editor: providedEditor, type, text, hideWhenUnavailable = false, className = '', disabled, onClick, children, ...buttonProps }, ref) => {
        const editor = useTiptapEditor(providedEditor);

        const { markInSchema, isDisabled, isActive, Icon, shortcutKey, formattedName } = useMarkState(editor, type, disabled);

        const handleClick = React.useCallback(
            (e: React.MouseEvent<HTMLButtonElement>) => {
                onClick?.(e);

                if (!e.defaultPrevented && !isDisabled && editor) {
                    toggleMark(editor, type);
                }
            },
            [onClick, isDisabled, editor, type],
        );

        const show = React.useMemo(() => {
            return shouldShowMarkButton({
                editor,
                type,
                hideWhenUnavailable,
                markInSchema,
            });
        }, [editor, type, hideWhenUnavailable, markInSchema]);

        if (!show || !editor || !editor.isEditable) {
            return null;
        }

        return (
            <Button
                type="button"
                className={className.trim()}
                disabled={isDisabled}
                data-style="ghost"
                data-active-state={isActive ? 'on' : 'off'}
                data-disabled={isDisabled}
                role="button"
                tabIndex={-1}
                aria-label={type}
                aria-pressed={isActive}
                tooltip={formattedName}
                shortcutKeys={shortcutKey}
                onClick={handleClick}
                {...buttonProps}
                ref={ref}
            >
                {children || (
                    <>
                        <Icon className="tiptap-button-icon" />
                        {text && <span className="tiptap-button-text">{text}</span>}
                    </>
                )}
            </Button>
        );
    },
);

MarkButton.displayName = 'MarkButton';

export default MarkButton;
