'use client';

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

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

// --- Icons ---
import { AlignCenterIcon } from '@/components/tiptap-icons/align-center-icon';
import { AlignJustifyIcon } from '@/components/tiptap-icons/align-justify-icon';
import { AlignLeftIcon } from '@/components/tiptap-icons/align-left-icon';
import { AlignRightIcon } from '@/components/tiptap-icons/align-right-icon';

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

export type TextAlign = 'left' | 'center' | 'right' | 'justify';

export interface TextAlignButtonProps extends ButtonProps {
    /**
     * The TipTap editor instance.
     */
    editor?: Editor | null;
    /**
     * The text alignment to apply.
     */
    align: TextAlign;
    /**
     * Optional text to display alongside the icon.
     */
    text?: string;
    /**
     * Whether the button should hide when the alignment is not available.
     * @default false
     */
    hideWhenUnavailable?: boolean;
}

export const textAlignIcons = {
    left: AlignLeftIcon,
    center: AlignCenterIcon,
    right: AlignRightIcon,
    justify: AlignJustifyIcon,
};

export const textAlignShortcutKeys: Partial<Record<TextAlign, string>> = {
    left: 'Ctrl-Shift-l',
    center: 'Ctrl-Shift-e',
    right: 'Ctrl-Shift-r',
    justify: 'Ctrl-Shift-j',
};

export const textAlignLabels: Record<TextAlign, string> = {
    left: 'Align left',
    center: 'Align center',
    right: 'Align right',
    justify: 'Align justify',
};

export function hasSetTextAlign(commands: ChainedCommands): commands is ChainedCommands & {
    setTextAlign: (align: TextAlign) => ChainedCommands;
} {
    return 'setTextAlign' in commands;
}

export function checkTextAlignExtension(editor: Editor | null): boolean {
    if (!editor) return false;

    const hasExtension = editor.extensionManager.extensions.some((extension) => extension.name === 'textAlign');

    if (!hasExtension) {
        console.warn('TextAlign extension is not available. ' + 'Make sure it is included in your editor configuration.');
    }

    return hasExtension;
}

export function canSetTextAlign(editor: Editor | null, align: TextAlign, alignAvailable: boolean): boolean {
    if (!editor || !alignAvailable) return false;

    try {
        return editor.can().setTextAlign(align);
    } catch {
        return false;
    }
}

export function isTextAlignActive(editor: Editor | null, align: TextAlign): boolean {
    if (!editor) return false;
    return editor.isActive({ textAlign: align });
}

export function setTextAlign(editor: Editor | null, align: TextAlign): boolean {
    if (!editor) return false;

    const chain = editor.chain().focus();
    if (hasSetTextAlign(chain)) {
        return chain.setTextAlign(align).run();
    }
    return false;
}

export function isTextAlignButtonDisabled(editor: Editor | null, alignAvailable: boolean, canAlign: boolean, userDisabled: boolean = false): boolean {
    if (!editor || !alignAvailable) return true;
    if (userDisabled) return true;
    if (!canAlign) return true;
    return false;
}

export function shouldShowTextAlignButton(editor: Editor | null, canAlign: boolean, hideWhenUnavailable: boolean): boolean {
    if (!editor?.isEditable) return false;
    if (hideWhenUnavailable && !canAlign) return false;
    return true;
}

export function useTextAlign(editor: Editor | null, align: TextAlign, disabled: boolean = false, hideWhenUnavailable: boolean = false) {
    const alignAvailable = React.useMemo(() => checkTextAlignExtension(editor), [editor]);

    const canAlign = React.useMemo(() => canSetTextAlign(editor, align, alignAvailable), [editor, align, alignAvailable]);

    const isDisabled = isTextAlignButtonDisabled(editor, alignAvailable, canAlign, disabled);
    const isActive = isTextAlignActive(editor, align);

    const handleAlignment = React.useCallback(() => {
        if (!alignAvailable || !editor || isDisabled) return false;
        return setTextAlign(editor, align);
    }, [alignAvailable, editor, isDisabled, align]);

    const shouldShow = React.useMemo(() => shouldShowTextAlignButton(editor, canAlign, hideWhenUnavailable), [editor, canAlign, hideWhenUnavailable]);

    const Icon = textAlignIcons[align];
    const shortcutKey = textAlignShortcutKeys[align];
    const label = textAlignLabels[align];

    return {
        alignAvailable,
        canAlign,
        isDisabled,
        isActive,
        handleAlignment,
        shouldShow,
        Icon,
        shortcutKey,
        label,
    };
}

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

        const { isDisabled, isActive, handleAlignment, shouldShow, Icon, shortcutKey, label } = useTextAlign(
            editor,
            align,
            disabled,
            hideWhenUnavailable,
        );

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

                if (!e.defaultPrevented && !disabled) {
                    handleAlignment();
                }
            },
            [onClick, disabled, handleAlignment],
        );

        if (!shouldShow || !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={label}
                aria-pressed={isActive}
                tooltip={label}
                shortcutKeys={shortcutKey}
                onClick={handleClick}
                {...buttonProps}
                ref={ref}
            >
                {children || (
                    <>
                        <Icon className="tiptap-button-icon" />
                        {text && <span className="tiptap-button-text">{text}</span>}
                    </>
                )}
            </Button>
        );
    },
);

TextAlignButton.displayName = 'TextAlignButton';

export default TextAlignButton;
