import { renderToString } from 'hono/jsx/dom/server';
import { afterEach, describe, expect, it, vi } from 'vitest';

import { Atom as RenderAtom, json as renderJson, RSS as RenderRSS, rss3 as renderRss3 } from '@/utils/render';
import Atom from '@/views/atom';
import jsonView from '@/views/json';
import RSS from '@/views/rss';
import rss3View from '@/views/rss3';

afterEach(() => {
    vi.resetModules();
    vi.unmock('@/config');
    vi.unmock('@/utils/debug-info');
    vi.unmock('@/utils/git-hash');
});

describe('view exports', () => {
    it('re-exports view helpers from render', () => {
        expect(RenderAtom).toBe(Atom);
        expect(RenderRSS).toBe(RSS);
        expect(renderJson).toBe(jsonView);
        expect(renderRss3).toBe(rss3View);
    });
});

describe('Atom view', () => {
    it('renders optional fields and media extensions', () => {
        const html = renderToString(
            <Atom
                data={{
                    title: 'Atom Feed',
                    link: 'https://example.com',
                    description: 'Atom Description',
                    language: 'zh',
                    lastBuildDate: '2024-01-01T00:00:00Z',
                    icon: 'https://example.com/icon.png',
                    logo: 'https://example.com/logo.png',
                    item: [
                        {
                            title: 'Item One',
                            link: 'https://example.com/one',
                            description: 'Entry One',
                            pubDate: '2024-01-01T00:00:00Z',
                            updated: '2024-01-02T00:00:00Z',
                            author: 'Author One',
                            category: 'News',
                            media: {
                                content: {
                                    url: 'https://example.com/video.mp4',
                                },
                            },
                            upvotes: 12,
                            downvotes: 1,
                            comments: 3,
                        },
                        {
                            title: 'Item Two',
                            link: 'https://example.com/two',
                            description: 'Entry Two',
                            category: ['Tech', 'AI'],
                            guid: 'guid-2',
                        },
                    ],
                }}
            />
        );

        expect(html).toContain('<icon>https://example.com/icon.png</icon>');
        expect(html).toContain('<logo>https://example.com/logo.png</logo>');
        expect(html).toContain('media:content');
        expect(html).toContain('term="News"');
        expect(html).toContain('term="Tech"');
        expect(html).toContain('rsshub:upvotes');
        expect(html).toContain('rsshub:downvotes');
        expect(html).toContain('rsshub:comments');
    });
});

describe('RSS view', () => {
    it('renders itunes, media, and telegram image variants', () => {
        const html = renderToString(
            <RSS
                data={{
                    title: 'RSS Feed',
                    link: 'https://t.me/s/rsshub',
                    atomlink: 'https://example.com/feed.xml',
                    description: 'RSS Description',
                    language: 'en',
                    lastBuildDate: '2024-01-01T00:00:00Z',
                    ttl: 60,
                    itunes_author: 'Podcast Author',
                    itunes_category: 'Tech',
                    itunes_explicit: 'true',
                    image: 'https://example.com/image.jpg',
                    item: [
                        {
                            title: 'Episode One',
                            link: 'https://example.com/one',
                            description: 'Episode One',
                            guid: 'episode-1',
                            pubDate: '2024-01-01T00:00:00Z',
                            author: 'Host',
                            image: 'https://example.com/episode.jpg',
                            itunes_item_image: 'https://example.com/itunes.jpg',
                            itunes_duration: '01:02:03',
                            enclosure_url: 'https://example.com/audio.mp3',
                            enclosure_length: '123',
                            enclosure_type: 'audio/mpeg',
                            category: 'Podcast',
                            media: {
                                content: {
                                    url: 'https://example.com/media.mp4',
                                },
                            },
                        },
                        {
                            title: 'Episode Two',
                            link: 'https://example.com/two',
                            description: 'Episode Two',
                            category: ['News', 'Updates'],
                        },
                    ],
                }}
            />
        );

        expect(html).toContain('xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"');
        expect(html).toContain('xmlns:media="http://search.yahoo.com/mrss/"');
        expect(html).toContain('<itunes:author>Podcast Author</itunes:author>');
        expect(html).toContain('itunes:category text="Tech"');
        expect(html).toContain('<itunes:explicit>true</itunes:explicit>');
        expect(html).toContain('<height>31</height>');
        expect(html).toContain('<width>88</width>');
        expect(html).toContain('media:content');
        expect(html).toContain('<itunes:image href="https://example.com/itunes.jpg"');
        expect(html).toContain('<enclosure url="https://example.com/audio.mp3"');
        expect(html).toContain('<category>Podcast</category>');
        expect(html).toContain('<category>News</category>');
    });
});

describe('JSON view', () => {
    it('renders summary, authors, tags, attachments, and extras', () => {
        const jsonOutput = jsonView({
            title: 'JSON Feed',
            link: 'https://example.com',
            feedLink: 'https://example.com/feed.json',
            description: 'JSON Description',
            language: 'en',
            author: 'Feed Author',
            image: 'https://example.com/icon.png',
            item: [
                {
                    title: 'Item One',
                    link: 'https://example.com/one',
                    description: 'Entry One',
                    guid: 'guid-1',
                    content: {
                        html: '<p>hello</p>',
                        text: 'hello',
                    },
                    image: 'https://example.com/image.jpg',
                    banner: 'https://example.com/banner.jpg',
                    pubDate: '2024-01-01T00:00:00Z',
                    updated: '2024-01-02T00:00:00Z',
                    author: 'Item Author',
                    category: ['Tech', 'AI'],
                    enclosure_url: 'https://example.com/audio.mp3',
                    enclosure_type: 'audio/mpeg',
                    enclosure_title: 'Audio Title',
                    enclosure_length: 123,
                    itunes_duration: 321,
                    _extra: { foo: 'bar' },
                },
            ],
        });

        const feed = JSON.parse(jsonOutput);

        expect(feed.version).toBe('https://jsonfeed.org/version/1.1');
        expect(feed.title).toBe('JSON Feed');
        expect(feed.home_page_url).toBe('https://example.com');
        expect(feed.feed_url).toBe('https://example.com/feed.json');
        expect(feed.description).toBe('JSON Description - Powered by RSSHub');
        expect(feed.authors).toEqual([{ name: 'Feed Author' }]);
        expect(feed.items).toHaveLength(1);
        expect(feed.items[0]).toMatchObject({
            id: 'guid-1',
            url: 'https://example.com/one',
            title: 'Item One',
            content_html: '<p>hello</p>',
            content_text: 'hello',
            summary: 'Entry One',
            image: 'https://example.com/image.jpg',
            banner_image: 'https://example.com/banner.jpg',
            date_published: '2024-01-01T00:00:00Z',
            date_modified: '2024-01-02T00:00:00Z',
            authors: [{ name: 'Item Author' }],
            tags: ['Tech', 'AI'],
            attachments: [
                {
                    url: 'https://example.com/audio.mp3',
                    mime_type: 'audio/mpeg',
                    title: 'Audio Title',
                    size_in_bytes: 123,
                    duration_in_seconds: 321,
                },
            ],
        });
        expect(feed.items[0]._extra).toEqual({ foo: 'bar' });
    });
});

describe('Index view', () => {
    const renderIndex = async (debugInfo: string | undefined, debugQuery: string | undefined) => {
        const debugData = {
            hitCache: 2,
            request: 10,
            etag: 3,
            error: 1,
            routes: {
                '/foo': 5,
                '/bar': 2,
            },
            paths: {
                '/foo?x=1': 4,
                '/bar?x=2': 1,
            },
            errorRoutes: {
                '/error': 2,
                '/fail': 1,
            },
            errorPaths: {
                '/error?x=1': 1,
                '/fail?x=2': 1,
            },
        };

        vi.doMock('@/config', () => ({
            config: {
                debugInfo,
                disallowRobot: true,
                nodeName: 'TestNode',
                cache: {
                    routeExpire: 120,
                },
            },
        }));
        vi.doMock('@/utils/debug-info', () => ({
            getDebugInfo: () => debugData,
        }));
        vi.doMock('@/utils/git-hash', () => ({
            gitHash: 'abc123',
            gitDate: new Date('2020-01-01T00:00:00Z'),
        }));

        const { default: Index } = await import('@/views/index');

        return renderToString(<Index debugQuery={debugQuery} />);
    };

    it('shows debug info when enabled', async () => {
        const html = await renderIndex('secret', 'secret');

        expect(html).toContain('Debug Info');
        expect(html).toContain('TestNode');
        expect(html).toContain('abc123');
        expect(html).toContain('5 /foo');
        expect(html).toContain('2 /error');
    });

    it('hides debug info when disabled', async () => {
        const html = await renderIndex('false', 'secret');

        expect(html).not.toContain('Debug Info');
    });
});
