import React, { useState, useEffect, useMemo } from 'react';
import { ServerContext } from '@/state/server';
import { checkBeautifySupport, beautifyFile } from './api/server/files/beautifyFile';
import { useFlashKey } from './useFlash';
import { Dialog } from '@/components/elements/dialog';
import { Button } from '@/components/elements/button/index';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSave } from '@fortawesome/free-solid-svg-icons';

const cx = (...parts: Array<string | false | null | undefined>) => parts.filter(Boolean).join(' ');

interface DiffLine {
    type: 'add' | 'remove' | 'context';
    text: string;
    oldNum?: number;
    newNum?: number;
}

function computeDiff(original: string, modified: string): DiffLine[] {
    const oldLines = original.split('\n');
    const newLines = modified.split('\n');
    const m = oldLines.length;
    const n = newLines.length;

    const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            dp[i][j] = oldLines[i - 1] === newLines[j - 1]
                ? dp[i - 1][j - 1] + 1
                : Math.max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    const result: DiffLine[] = [];
    let i = m, j = n;
    const stack: DiffLine[] = [];
    while (i > 0 || j > 0) {
        if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
            stack.push({ type: 'context', text: oldLines[i - 1], oldNum: i, newNum: j });
            i--; j--;
        } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
            stack.push({ type: 'add', text: newLines[j - 1], newNum: j });
            j--;
        } else {
            stack.push({ type: 'remove', text: oldLines[i - 1], oldNum: i });
            i--;
        }
    }
    stack.reverse();

    const contextWindow = 3;
    const chunks: DiffLine[][] = [];
    let current: DiffLine[] = [];
    let lastChangeIdx = -100;

    for (let idx = 0; idx < stack.length; idx++) {
        const line = stack[idx];
        if (line.type !== 'context') {
            const start = Math.max(0, idx - contextWindow);
            for (let k = start; k < idx; k++) {
                if (k > lastChangeIdx + contextWindow && stack[k].type === 'context') {
                    if (current.length > 0 && !current.some(l => l === stack[k])) {
                        current.push(stack[k]);
                    }
                }
            }
            current.push(line);
            lastChangeIdx = idx;
        } else if (idx <= lastChangeIdx + contextWindow) {
            current.push(line);
        } else if (current.length > 0 && idx === lastChangeIdx + contextWindow + 1) {
            chunks.push(current);
            current = [];
        }
    }
    if (current.length > 0) chunks.push(current);

    if (chunks.length === 0) return [{ type: 'context', text: 'No changes', oldNum: 0, newNum: 0 }];

    for (const chunk of chunks) {
        result.push(...chunk);
        result.push({ type: 'context', text: '---', oldNum: undefined, newNum: undefined });
    }
    if (result.length > 0 && result[result.length - 1].text === '---') result.pop();

    return result;
}

interface FileToolbarProps {
    selectedFile: string;
    content?: string;
    originalContent?: string;
    hasUnsavedChanges: boolean;
    isSaving: boolean;
    readOnly?: boolean;
    onSave: () => void;
    onReset: () => void;
    onContentUpdate?: (content: string) => void;
    canPreviewMarkdown?: boolean;
    markdownPreviewVisible?: boolean;
    onToggleMarkdownPreview?: () => void;
    onShowChanges?: () => void;
    changesVisible?: boolean;
    compact?: boolean;
}

const FileToolbar: React.FC<FileToolbarProps> = ({
    selectedFile,
    content,
    originalContent,
    hasUnsavedChanges,
    isSaving,
    readOnly = false,
    onSave,
    onReset,
    onContentUpdate,
    canPreviewMarkdown = false,
    markdownPreviewVisible = false,
    onToggleMarkdownPreview,
    onShowChanges,
    changesVisible = false,
    compact = false,
}) => {
    const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
    const { clearFlashes, clearAndAddHttpError } = useFlashKey('better-files');
    const [canBeautify, setCanBeautify] = useState(false);
    const [isBeautifying, setIsBeautifying] = useState(false);
    const [showDiff, setShowDiff] = useState(false);

    useEffect(() => {
        if (selectedFile) {
            checkBeautifySupport(uuid, selectedFile)
                .then(response => setCanBeautify(response.supported))
                .catch(() => setCanBeautify(false));
        }
    }, [selectedFile, uuid]);

    const handleBeautify = async () => {
        if (!selectedFile || !onContentUpdate) return;

        setIsBeautifying(true);
        try {
            const response = await beautifyFile(uuid, selectedFile, content);
            if (response.content && onContentUpdate) {
                const newContent = typeof response.content === 'string'
                    ? response.content
                    : JSON.stringify(response.content, null, 4);
                onContentUpdate(newContent);
                clearFlashes();
            }
        } catch (error: any) {
            clearAndAddHttpError(error);
        } finally {
            setIsBeautifying(false);
        }
    };

    const diffLines = useMemo(() => {
        if (!showDiff || originalContent === undefined || content === undefined) return [];
        return computeDiff(originalContent, content);
    }, [showDiff, originalContent, content]);

    const diffStats = useMemo(() => {
        const added = diffLines.filter(l => l.type === 'add').length;
        const removed = diffLines.filter(l => l.type === 'remove').length;
        return { added, removed };
    }, [diffLines]);

    const size = compact ? Button.Sizes.Small : Button.Sizes.Default;

    return (
        <>
            <div className={compact
                ? 'w-full max-w-full min-w-0 flex items-center gap-1 flex-nowrap overflow-x-auto overflow-y-hidden'
                : 'w-full max-w-full min-w-0 flex items-center gap-2 flex-wrap overflow-hidden'
            }
                style={compact ? ({ WebkitOverflowScrolling: 'touch', touchAction: 'pan-x' } as React.CSSProperties) : undefined}
            >
                {canBeautify && (
                    <Button
                        type='button'
                        size={size}
                        onClick={handleBeautify}
                        disabled={readOnly || isSaving || isBeautifying}
                    >
                        <svg className='w-4 h-4 mr-1' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
                            <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01' />
                        </svg>
                        {isBeautifying ? 'Formatting...' : 'Format'}
                    </Button>
                )}

                {canPreviewMarkdown && onToggleMarkdownPreview && (
                    <Button.Text
                        type='button'
                        size={size}
                        onClick={onToggleMarkdownPreview}
                        title={markdownPreviewVisible ? 'Hide Markdown Preview' : 'Show Markdown Preview'}
                        className={cx(
                            'flex-shrink-0',
                            markdownPreviewVisible && 'bg-neutral-600 text-neutral-100 border-neutral-500'
                        )}
                    >
                        <svg className='w-4 h-4 mr-1' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
                            {markdownPreviewVisible ? (
                                <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M4 6h16M4 12h10M4 18h16' />
                            ) : (
                                <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M15 12a3 3 0 11-6 0 3 3 0 016 0zm7.5 0c-1.7 3.5-5.5 6-10.5 6S3.2 15.5 1.5 12C3.2 8.5 7 6 12 6s8.8 2.5 10.5 6z' />
                            )}
                        </svg>
                        {compact ? 'Preview' : markdownPreviewVisible ? 'Hide Preview' : 'Preview'}
                    </Button.Text>
                )}

                <Button
                    type='button'
                    size={size}
                    onClick={() => onSave()}
                    disabled={readOnly || !hasUnsavedChanges || isSaving}
                >
                    <FontAwesomeIcon icon={faSave} fixedWidth className='mr-1 text-[13px]' />
                    {isSaving ? 'Saving...' : 'Save'}
                </Button>

                {hasUnsavedChanges && originalContent !== undefined && (
                    <Button.Text
                        type='button'
                        size={size}
                        onClick={() => {
                            if (onShowChanges) {
                                onShowChanges();
                                return;
                            }
                            setShowDiff(true);
                        }}
                        title={changesVisible ? 'Hide changes' : 'Show changes'}
                        className={cx(
                            'flex-shrink-0',
                            changesVisible && 'bg-neutral-600 text-neutral-100 border-neutral-500'
                        )}
                    >
                        <svg className='w-4 h-4 mr-1' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
                            <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' />
                        </svg>
                        Changes
                    </Button.Text>
                )}

                <Button.Text
                    type='button'
                    size={size}
                    onClick={onReset}
                    disabled={!hasUnsavedChanges || isSaving}
                >
                    <svg className='w-4 h-4 mr-1' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
                        <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15' />
                    </svg>
                    Reset
                </Button.Text>
            </div>

            <Dialog open={showDiff} onClose={() => setShowDiff(false)} title={`Changes - ${selectedFile.split('/').pop()}`}>
                <div className='flex items-center gap-3 mb-3'>
                    {diffStats.added > 0 && (
                        <span className='text-xs font-medium text-green-400'>+{diffStats.added} added</span>
                    )}
                    {diffStats.removed > 0 && (
                        <span className='text-xs font-medium text-red-400'>-{diffStats.removed} removed</span>
                    )}
                </div>
                <div
                    className='rounded overflow-auto bg-neutral-800 border border-neutral-700 font-mono text-xs'
                    style={{ maxHeight: '60vh' }}
                >
                    {diffLines.map((line, idx) => (
                        <div
                            key={idx}
                            className={
                                line.type === 'add'
                                    ? 'bg-green-500/10 text-green-300'
                                    : line.type === 'remove'
                                    ? 'bg-red-500/10 text-red-300'
                                    : 'text-neutral-400'
                            }
                            style={{ padding: '1px 8px', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}
                        >
                            <span className='inline-block w-5 text-right mr-2 text-neutral-600 select-none'>
                                {line.type === 'remove' ? (line.oldNum ?? '') : line.type === 'add' ? (line.newNum ?? '') : (line.oldNum ?? '')}
                            </span>
                            <span className='inline-block w-3 select-none'>
                                {line.type === 'add' ? '+' : line.type === 'remove' ? '-' : ' '}
                            </span>
                            {line.text}
                        </div>
                    ))}
                </div>
                <Dialog.Footer>
                    <Button.Text onClick={() => setShowDiff(false)}>Close</Button.Text>
                </Dialog.Footer>
            </Dialog>
        </>
    );
};

export default FileToolbar;
