import * as cheerio from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';

import type { Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';

const renderMedia = (media) => {
    const video = {
        cover: media.posterImages?.full?.href || media.posterImages?.default?.href,
        src: media.links?.source.mezzanine?.href || media.links?.source.HD?.href || media.links?.source.full?.href || media.links?.source.href,
        title: media.title,
        description: media.description,
    };
    const image = {
        src: media.url,
        alt: media.alt,
        caption: media.caption,
        credit: media.credit,
    };

    return renderToString(
        <>
            {video.src ? (
                <>
                    <video controls preload="metadata" width="100%" height="auto" cover={video.cover}>
                        <source src={video.src} type="video/mp4" />
                    </video>
                    {video.title || video.description ? (
                        <div>
                            {video.title ? (
                                <div>
                                    <b>{video.title}</b>
                                </div>
                            ) : null}
                            {video.description ? <p>{video.description}</p> : null}
                        </div>
                    ) : null}
                </>
            ) : null}
            {image.src ? (
                <figure>
                    {image.alt ? <img src={image.src} alt={image.alt} /> : <img src={image.src} />}
                    {image.caption ? <figcaption>{image.caption}</figcaption> : null}
                    {image.credit ? <cite>{image.credit}</cite> : null}
                </figure>
            ) : null}
        </>
    );
};

const junkPattern = /inline\d+|alsosee/;
const mediaPattern = /(photo|video)(\d+)/;

export const route: Route = {
    path: '/news/:sport',
    name: 'News',
    maintainers: ['weijianduan0302'],
    example: '/espn/news/nba',
    categories: ['traditional-media'],
    parameters: { sport: 'sport category, can be nba, nfl, mlb, nhl etc.' },
    description: `Get the news feed of the sport you love on ESPN.
| Sport                |  sport  |  Sport         |  sport  |
|----------------------|---------|----------------|---------|
| 🏀 NBA                | nba     | 🎾 Tennis       | tennis  |
| 🏀 WNBA               | wnba    | ⛳️ Golf         | golf    |
| 🏈 NFL                | nfl     | 🏏 Cricket      | cricket |
| ⚾️ MLB                | mlb     | ⚽️ Soccer       | soccer  |
| 🏒 NHL                | nhl     | 🏎️ F1           | f1      |
| ⛹️ College Basketball | ncb      | 🥊 MMA          | mma     |
| 🏟️️ College Football   | ncf     | 🏈 UFL          | ufl     |
| 🏉 Rugby              | rugby   | 🃏 Poker        | poker   |`,
    radar: [
        {
            source: ['espn.com/:sport*'],
            target: '/news/:sport',
        },
    ],
    handler: async (ctx) => {
        const { sport = 'nba' } = ctx.req.param();
        const response = await ofetch(`https://onefeed.fan.api.espn.com/apis/v3/cached/contentEngine/oneFeed/leagues/${sport}?offset=0`, {
            headers: {
                accept: 'application/json',
            },
        });

        const handledTypes = new Set(['HeadlineNews', 'Story', 'Media', 'Shortstop']);
        const list = response.feed
            .filter((item) => handledTypes.has(item.data.now[0].type))
            .map((item) => {
                const itemDetail = item.data.now[0];
                const itemType = itemDetail.type;

                return {
                    title: itemDetail.headline,
                    link: itemDetail.links.web.href,
                    author: itemDetail.byline,
                    pubDate: item.date,
                    // for videos and shortstops, no need to extract full text below
                    description: itemType === 'Media' ? renderMedia(itemDetail.video[0]) : itemType === 'Shortstop' ? itemDetail.headline : '',
                };
            });

        const items = await Promise.all(
            list.map((item) =>
                cache.tryGet(item.link, async () => {
                    if (item.description === '') {
                        const article = await ofetch(`${item.link}?xhr=1`, {
                            headers: {
                                accept: 'application/json',
                            },
                        });

                        const $ = cheerio.load(article.content.story, null, false);
                        $('*').each((_, ele) => {
                            if (junkPattern.test(ele.name)) {
                                $(ele).remove();
                            }
                            if (mediaPattern.test(ele.name)) {
                                const mediaType = ele.name.match(mediaPattern)[1] === 'photo' ? 'images' : 'video';
                                const mediaIndex = Number.parseInt(ele.name.match(mediaPattern)[2]) - 1;
                                const media = article.content[mediaType][mediaIndex];
                                if (media) {
                                    $(ele).replaceWith(renderMedia(media));
                                } else {
                                    $(ele).remove();
                                }
                            }
                        });

                        item.description = $.html();
                    }

                    return item;
                })
            )
        );

        return {
            title: `ESPN ${sport.toUpperCase()} News`,
            link: `https://www.espn.com/espn/rss/${sport}/news`,
            item: items,
        };
    },
};
