import React from 'react';
import GreyRowBox from '@/components/elements/GreyRowBox';

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

export const isMarkdownFile = (path?: string | null): boolean => /\.(md|markdown|mdown|mkdn|mkd)$/i.test(path || '');

const cleanMarkdownUrl = (value: string, image = false): string | null => {
    const trimmed = value.trim().replace(/^<|>$/g, '');
    if (!trimmed || /[\u0000-\u001f\u007f]/.test(trimmed)) return null;
    if (/^(javascript|vbscript|file):/i.test(trimmed)) return null;
    if (image && /^data:/i.test(trimmed)) return null;

    const hasProtocol = /^[a-z][a-z0-9+.-]*:/i.test(trimmed);
    if (!hasProtocol) return trimmed;

    try {
        const parsed = new URL(trimmed);
        if (image) return ['http:', 'https:'].includes(parsed.protocol) ? trimmed : null;
        return ['http:', 'https:', 'mailto:'].includes(parsed.protocol) ? trimmed : null;
    } catch {
        return null;
    }
};

const findNextInlineToken = (text: string, from: number): number => {
    const candidates = ['`', '![', '[', '**', '__', '*', '_']
        .map((token) => text.indexOf(token, from))
        .filter((index) => index >= 0);

    return candidates.length > 0 ? Math.min(...candidates) : text.length;
};

const renderInline = (text: string, keyPrefix: string): React.ReactNode[] => {
    const nodes: React.ReactNode[] = [];
    let index = 0;

    const pushText = (value: string) => {
        if (value) nodes.push(value);
    };

    while (index < text.length) {
        if (text[index] === '`') {
            const end = text.indexOf('`', index + 1);
            if (end > index + 1) {
                nodes.push(
                    <code key={`${keyPrefix}-code-${index}`} className='rounded bg-neutral-800 px-1 py-0.5 font-mono text-[0.9em] text-neutral-100'>
                        {text.slice(index + 1, end)}
                    </code>
                );
                index = end + 1;
                continue;
            }
        }

        if (text.startsWith('[![', index)) {
            const altEnd = text.indexOf(']', index + 3);
            const imageUrlStart = altEnd >= 0 && text[altEnd + 1] === '(' ? altEnd + 2 : -1;
            const imageUrlEnd = imageUrlStart >= 0 ? text.indexOf(')', imageUrlStart) : -1;
            const linkUrlStart = imageUrlEnd >= 0 && text[imageUrlEnd + 1] === ']' && text[imageUrlEnd + 2] === '(' ? imageUrlEnd + 3 : -1;
            const linkUrlEnd = linkUrlStart >= 0 ? text.indexOf(')', linkUrlStart) : -1;

            if (altEnd >= index + 3 && imageUrlEnd > imageUrlStart && linkUrlEnd > linkUrlStart) {
                const alt = text.slice(index + 3, altEnd);
                const imageUrl = cleanMarkdownUrl(text.slice(imageUrlStart, imageUrlEnd), true);
                const linkUrl = cleanMarkdownUrl(text.slice(linkUrlStart, linkUrlEnd));
                const image = imageUrl ? (
                    <img
                        src={imageUrl}
                        alt={alt}
                        className='my-3 max-h-80 max-w-full rounded border border-neutral-700 bg-neutral-900 object-contain'
                        loading='lazy'
                        referrerPolicy='no-referrer'
                    />
                ) : (
                    <span className='text-neutral-500'>{alt}</span>
                );

                nodes.push(
                    linkUrl ? (
                        <a
                            key={`${keyPrefix}-linked-image-${index}`}
                            href={linkUrl}
                            target='_blank'
                            rel='noreferrer noopener'
                            className='inline-block max-w-full'
                        >
                            {image}
                        </a>
                    ) : (
                        <React.Fragment key={`${keyPrefix}-linked-image-blocked-${index}`}>{image}</React.Fragment>
                    )
                );
                index = linkUrlEnd + 1;
                continue;
            }
        }

        if (text.startsWith('![', index)) {
            const labelEnd = text.indexOf(']', index + 2);
            const urlStart = labelEnd >= 0 && text[labelEnd + 1] === '(' ? labelEnd + 2 : -1;
            const urlEnd = urlStart >= 0 ? text.indexOf(')', urlStart) : -1;
            if (labelEnd >= index + 2 && urlEnd > urlStart) {
                const alt = text.slice(index + 2, labelEnd);
                const url = cleanMarkdownUrl(text.slice(urlStart, urlEnd), true);
                nodes.push(
                    url ? (
                        <img
                            key={`${keyPrefix}-image-${index}`}
                            src={url}
                            alt={alt}
                            className='my-3 max-h-80 max-w-full rounded border border-neutral-700 bg-neutral-900 object-contain'
                            loading='lazy'
                            referrerPolicy='no-referrer'
                        />
                    ) : (
                        <span key={`${keyPrefix}-image-blocked-${index}`} className='text-neutral-500'>
                            {alt}
                        </span>
                    )
                );
                index = urlEnd + 1;
                continue;
            }
        }

        if (text[index] === '[') {
            const labelEnd = text.indexOf(']', index + 1);
            const urlStart = labelEnd >= 0 && text[labelEnd + 1] === '(' ? labelEnd + 2 : -1;
            const urlEnd = urlStart >= 0 ? text.indexOf(')', urlStart) : -1;
            if (labelEnd > index + 1 && urlEnd > urlStart) {
                const label = text.slice(index + 1, labelEnd);
                const url = cleanMarkdownUrl(text.slice(urlStart, urlEnd));
                nodes.push(
                    url ? (
                        <a
                            key={`${keyPrefix}-link-${index}`}
                            href={url}
                            target='_blank'
                            rel='noreferrer noopener'
                            className='text-blue-400 underline decoration-blue-500/40 underline-offset-2 hover:text-blue-300'
                        >
                            {renderInline(label, `${keyPrefix}-link-${index}`)}
                        </a>
                    ) : (
                        <span key={`${keyPrefix}-link-blocked-${index}`}>{label}</span>
                    )
                );
                index = urlEnd + 1;
                continue;
            }
        }

        const strongToken = text.startsWith('**', index) ? '**' : text.startsWith('__', index) ? '__' : null;
        if (strongToken) {
            const end = text.indexOf(strongToken, index + 2);
            if (end > index + 2) {
                nodes.push(
                    <strong key={`${keyPrefix}-strong-${index}`} className='font-semibold text-neutral-100'>
                        {renderInline(text.slice(index + 2, end), `${keyPrefix}-strong-${index}`)}
                    </strong>
                );
                index = end + 2;
                continue;
            }
        }

        const emphasisToken = text[index] === '*' || text[index] === '_' ? text[index] : null;
        if (emphasisToken) {
            const end = text.indexOf(emphasisToken, index + 1);
            if (end > index + 1) {
                nodes.push(
                    <em key={`${keyPrefix}-em-${index}`} className='italic text-neutral-200'>
                        {renderInline(text.slice(index + 1, end), `${keyPrefix}-em-${index}`)}
                    </em>
                );
                index = end + 1;
                continue;
            }
        }

        const next = findNextInlineToken(text, index + 1);
        pushText(text.slice(index, next));
        index = next;
    }

    return nodes;
};

const isFence = (line: string) => /^```/.test(line.trim());
const isHeading = (line: string) => /^#{1,6}\s+/.test(line);
const isHorizontalRule = (line: string) => /^(\s*)([-*_])(\s*\2){2,}\s*$/.test(line);
const isUnorderedListItem = (line: string) => /^\s*[-*+]\s+/.test(line);
const isOrderedListItem = (line: string) => /^\s*\d+\.\s+/.test(line);
const isBlockQuote = (line: string) => /^\s*>\s?/.test(line);
const isTableDivider = (line: string) => /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(line);

const splitTableCells = (line: string): string[] => {
    const trimmed = line.trim().replace(/^\|/, '').replace(/\|$/, '');
    return trimmed.split('|').map((cell) => cell.trim());
};

const isTableStart = (lines: string[], index: number): boolean => {
    if (!lines[index]?.includes('|') || !lines[index + 1]) return false;
    return isTableDivider(lines[index + 1]);
};

const isBlockStart = (lines: string[], index: number): boolean => {
    const line = lines[index] || '';
    return (
        isFence(line) ||
        isHeading(line) ||
        isHorizontalRule(line) ||
        isUnorderedListItem(line) ||
        isOrderedListItem(line) ||
        isBlockQuote(line) ||
        isTableStart(lines, index)
    );
};

interface MarkdownPreviewProps {
    content: string;
    filePath?: string | null;
    className?: string;
}

const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({ content, filePath, className }) => {
    const lines = content.replace(/\r\n?/g, '\n').split('\n');
    const rendered: React.ReactNode[] = [];
    let index = 0;

    while (index < lines.length) {
        const line = lines[index];
        const trimmed = line.trim();

        if (!trimmed) {
            index++;
            continue;
        }

        if (isFence(line)) {
            const language = trimmed.slice(3).trim();
            const body: string[] = [];
            index++;
            while (index < lines.length && !isFence(lines[index])) {
                body.push(lines[index]);
                index++;
            }
            if (index < lines.length) index++;

            rendered.push(
                <div key={`code-${index}`} className='my-4 overflow-hidden rounded border border-neutral-700 bg-neutral-900'>
                    {language && <div className='border-b border-neutral-700 px-3 py-2 text-xs text-neutral-500'>{language}</div>}
                    <pre className='overflow-x-auto p-3 text-xs text-neutral-200'>
                        <code className='font-mono'>{body.join('\n')}</code>
                    </pre>
                </div>
            );
            continue;
        }

        if (isHeading(line)) {
            const match = /^(#{1,6})\s+(.+)$/.exec(line);
            const level = Math.min(match?.[1].length || 1, 6);
            const text = match?.[2] || line;
            const HeadingTag = `h${level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
            const classes = [
                'mt-6 mb-3 font-semibold text-neutral-100',
                level === 1 && 'text-2xl border-b border-neutral-700 pb-2',
                level === 2 && 'text-xl border-b border-neutral-700 pb-2',
                level === 3 && 'text-lg',
                level >= 4 && 'text-sm text-neutral-200',
            ];

            rendered.push(
                <HeadingTag key={`heading-${index}`} className={cx(...classes)}>
                    {renderInline(text, `heading-${index}`)}
                </HeadingTag>
            );
            index++;
            continue;
        }

        if (isHorizontalRule(line)) {
            rendered.push(<hr key={`hr-${index}`} className='my-5 border-neutral-700' />);
            index++;
            continue;
        }

        if (isTableStart(lines, index)) {
            const headers = splitTableCells(lines[index]);
            const rows: string[][] = [];
            index += 2;
            while (index < lines.length && lines[index].includes('|') && lines[index].trim()) {
                rows.push(splitTableCells(lines[index]));
                index++;
            }

            rendered.push(
                <div key={`table-${index}`} className='my-4 overflow-x-auto rounded border border-neutral-700'>
                    <table className='min-w-full divide-y divide-neutral-700 text-left text-sm'>
                        <thead className='bg-neutral-900'>
                            <tr>
                                {headers.map((header, cellIndex) => (
                                    <th key={`${header}-${cellIndex}`} className='px-3 py-2 text-xs font-semibold text-neutral-400'>
                                        {renderInline(header, `table-h-${index}-${cellIndex}`)}
                                    </th>
                                ))}
                            </tr>
                        </thead>
                        <tbody className='divide-y divide-neutral-700 bg-neutral-800'>
                            {rows.map((row, rowIndex) => (
                                <tr key={`row-${rowIndex}`}>
                                    {headers.map((_, cellIndex) => (
                                        <td key={`cell-${rowIndex}-${cellIndex}`} className='px-3 py-2 text-neutral-300'>
                                            {renderInline(row[cellIndex] || '', `table-c-${index}-${rowIndex}-${cellIndex}`)}
                                        </td>
                                    ))}
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            );
            continue;
        }

        if (isUnorderedListItem(line) || isOrderedListItem(line)) {
            const ordered = isOrderedListItem(line);
            const items: string[] = [];
            while (index < lines.length && (ordered ? isOrderedListItem(lines[index]) : isUnorderedListItem(lines[index]))) {
                items.push(lines[index].replace(ordered ? /^\s*\d+\.\s+/ : /^\s*[-*+]\s+/, ''));
                index++;
            }
            const ListTag = (ordered ? 'ol' : 'ul') as 'ol' | 'ul';
            rendered.push(
                <ListTag key={`list-${index}`} className={cx('my-3 space-y-1 pl-6 text-sm text-neutral-300', ordered ? 'list-decimal' : 'list-disc')}>
                    {items.map((item, itemIndex) => (
                        <li key={`item-${itemIndex}`}>{renderInline(item, `list-${index}-${itemIndex}`)}</li>
                    ))}
                </ListTag>
            );
            continue;
        }

        if (isBlockQuote(line)) {
            const quoteLines: string[] = [];
            while (index < lines.length && isBlockQuote(lines[index])) {
                quoteLines.push(lines[index].replace(/^\s*>\s?/, ''));
                index++;
            }
            rendered.push(
                <blockquote key={`quote-${index}`} className='my-4 border-l-4 border-neutral-600 pl-3 text-sm text-neutral-400'>
                    {quoteLines.map((quoteLine, quoteIndex) => (
                        <p key={`quote-line-${quoteIndex}`} className='my-1'>
                            {renderInline(quoteLine, `quote-${index}-${quoteIndex}`)}
                        </p>
                    ))}
                </blockquote>
            );
            continue;
        }

        const paragraph: string[] = [];
        while (index < lines.length && lines[index].trim() && !isBlockStart(lines, index)) {
            paragraph.push(lines[index].trim());
            index++;
        }

        rendered.push(
            <p key={`paragraph-${index}`} className='my-3 text-sm leading-6 text-neutral-300'>
                {renderInline(paragraph.join(' '), `paragraph-${index}`)}
            </p>
        );
    }

    return (
        <GreyRowBox className={cx('flex flex-col !items-stretch !p-0 overflow-hidden', className)} $hoverable={false}>
            <div className='flex items-center justify-between gap-3 border-b border-neutral-700 bg-neutral-900 px-3 py-2'>
                <div className='min-w-0'>
                    <p className='text-xs font-medium text-neutral-400'>Markdown preview</p>
                    {filePath && <p className='truncate text-xs text-neutral-300'>{filePath.split('/').pop()}</p>}
                </div>
            </div>
            <div className='flex-1 overflow-auto px-4 py-3 text-neutral-300'>
                {rendered.length > 0 ? rendered : <p className='text-sm text-neutral-500'>Nothing to preview.</p>}
            </div>
        </GreyRowBox>
    );
};

export default MarkdownPreview;
