export type ArticleMedia = {
    id: number;
    url: string;
    mime_type: string | null;
    original_name: string | null;
    is_image: boolean;
};

type ArticleBodyProps = {
    body: string;
    media: ArticleMedia[];
    contentFont: 'sans' | 'serif' | 'technical';
};

export function ArticleBody({ body, media, contentFont }: ArticleBodyProps) {
    const mediaById = new Map(media.map((item) => [item.id, item]));
    const referencedMediaIds = new Set<number>();
    const lines = body.split(/\r?\n/);
    const blocks: React.ReactNode[] = [];

    for (let index = 0; index < lines.length; index++) {
        const line = lines[index];
        const trimmed = line.trim();

        if (trimmed === '') {
            continue;
        }

        const mediaMatch = trimmed.match(/^\[media:(\d+)\]$/);

        if (mediaMatch) {
            const mediaId = Number(mediaMatch[1]);
            const mediaItem = mediaById.get(mediaId);

            referencedMediaIds.add(mediaId);

            if (mediaItem) {
                blocks.push(
                    <MediaBlock
                        key={`media-${mediaId}-${index}`}
                        media={mediaItem}
                    />,
                );
            }

            continue;
        }

        if (trimmed.startsWith('## ')) {
            blocks.push(
                <h2
                    key={`h2-${index}`}
                    className="text-2xl font-semibold text-main"
                >
                    {renderInline(trimmed.slice(3))}
                </h2>,
            );

            continue;
        }

        if (trimmed.startsWith('### ')) {
            blocks.push(
                <h3
                    key={`h3-${index}`}
                    className="text-xl font-semibold text-main"
                >
                    {renderInline(trimmed.slice(4))}
                </h3>,
            );

            continue;
        }

        if (trimmed.startsWith('> ')) {
            blocks.push(
                <blockquote
                    key={`quote-${index}`}
                    className="border-l-4 border-primary bg-surface px-4 py-3 text-main"
                >
                    {renderInline(trimmed.slice(2))}
                </blockquote>,
            );

            continue;
        }

        if (trimmed.startsWith('- ')) {
            const items: string[] = [];

            while (
                index < lines.length &&
                lines[index].trim().startsWith('- ')
            ) {
                items.push(lines[index].trim().slice(2));
                index++;
            }

            index--;

            blocks.push(
                <ul
                    key={`list-${index}`}
                    className="list-disc space-y-1 pl-5 text-main"
                >
                    {items.map((item, itemIndex) => (
                        <li key={`${item}-${itemIndex}`}>
                            {renderInline(item)}
                        </li>
                    ))}
                </ul>,
            );

            continue;
        }

        blocks.push(
            <p key={`p-${index}`} className="leading-7 text-main">
                {renderInline(trimmed)}
            </p>,
        );
    }

    const unreferencedMedia = media.filter(
        (item) => !referencedMediaIds.has(item.id),
    );

    return (
        <div className={['space-y-5', fontClass(contentFont)].join(' ')}>
            {blocks}

            {unreferencedMedia.length > 0 ? (
                <div className="grid gap-4 sm:grid-cols-2">
                    {unreferencedMedia.map((item) => (
                        <MediaBlock key={`gallery-${item.id}`} media={item} />
                    ))}
                </div>
            ) : null}
        </div>
    );
}

function MediaBlock({ media }: { media: ArticleMedia }) {
    return (
        <figure className="overflow-hidden rounded-xl border border-border bg-surface">
            {media.is_image ? (
                <img
                    src={media.url}
                    alt={media.original_name || 'Article media'}
                    className="max-h-[520px] w-full object-cover"
                />
            ) : (
                <video
                    src={media.url}
                    controls
                    className="max-h-[520px] w-full bg-black object-contain"
                />
            )}
            {media.original_name ? (
                <figcaption className="px-3 py-2 text-xs text-muted">
                    {media.original_name}
                </figcaption>
            ) : null}
        </figure>
    );
}

function renderInline(text: string): React.ReactNode[] {
    return text.split(/(\*\*[^*]+\*\*|\*[^*]+\*)/g).map((part, index) => {
        if (part.startsWith('**') && part.endsWith('**')) {
            return (
                <strong key={`${part}-${index}`}>{part.slice(2, -2)}</strong>
            );
        }

        if (part.startsWith('*') && part.endsWith('*')) {
            return <em key={`${part}-${index}`}>{part.slice(1, -1)}</em>;
        }

        return part;
    });
}

function fontClass(contentFont: ArticleBodyProps['contentFont']): string {
    if (contentFont === 'serif') {
        return 'font-serif';
    }

    if (contentFont === 'technical') {
        return 'font-mono';
    }

    return 'font-sans';
}
