import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
import ConfigNotFoundError from '@/errors/types/config-not-found';

const allowSiteList = ['mastodon.social', 'pawoo.net', 'fosstodon.org', config.mastodon.apiHost].filter(Boolean);

const apiHeaders = (site) => {
    const { accessToken, apiHost } = config.mastodon;
    // avoid sending API token to other sites
    return accessToken && site === apiHost ? { Authorization: `Bearer ${accessToken}` } : {};
};

const mediaParse = (media_attachments) =>
    media_attachments
        .map((item) => {
            const selectedUrl = item.remote_url ?? item.url;
            const description = item.description ?? '';
            switch (item.type) {
                case 'gifv':
                    return `<br><video src="${selectedUrl}" autoplay loop>gif ${description}</video>`;
                case 'video':
                    return `<br><video src="${selectedUrl}" controls loop>video ${description}</video>`;
                case 'image':
                    return `<br><img src="${selectedUrl}" alt="image ${description}">`;
                case 'audio':
                    return `<br><audio controls src="${selectedUrl}">audio ${description}</audio>`;
                case 'unknown':
                default:
                    return `<br><a href="${selectedUrl}">media ${description}</a>`;
            }
        })
        .join('');

const parseStatuses = (data) =>
    data.map((item) => {
        // docs on: https://docs.joinmastodon.org/entities/status/

        const accountRepostedBy = item.reblog ? item.account : null;
        item = item.reblog ?? item;

        const content = item.content ? item.content.replaceAll(/<span.*?>|<\/span.*?>/gm, '') : '';
        const contentRemovedHtml = content.replaceAll(/<(?:.|\n)*?>/gm, '\n');

        const author = `${item.account.display_name} (@${item.account.acct})`;
        const link = item.url;
        const media = mediaParse(item.media_attachments);

        const titleAuthor = accountRepostedBy ? `Re @${accountRepostedBy.username}` : `@${item.account.username}`;
        const titleText = item.sensitive === true ? `(CW) ${item.spoiler_text}` : contentRemovedHtml;
        const title = `${titleAuthor}: "${titleText}"`;

        return {
            title,
            author,
            description: item.spoiler_text + '<hr />' + content + media,
            pubDate: parseDate(item.created_at),
            link,
            guid: item.uri,
        };
    });

async function getAccountStatuses(site, account_id, only_media) {
    const statuses_url = `https://${site}/api/v1/accounts/${account_id}/statuses?only_media=${only_media}`;
    const statuses_response = await got({
        method: 'get',
        url: statuses_url,
        headers: apiHeaders(site),
    });
    const data = statuses_response.data;

    let account_data;
    if (data.length !== 0 && data[0].account !== null) {
        account_data = data[0].account;
    } else {
        const account_url = `https://${site}/api/v1/accounts/${account_id}`;
        const account_response = await got({
            method: 'get',
            url: account_url,
            headers: apiHeaders(site),
        });
        account_data = account_response.data;
    }

    return { account_data, data };
}

async function getAccountIdByAcct(acct) {
    const mastodonConfig = config.mastodon;

    // acctHost is from the acct param of the request, and acctDomain is from either acctHost or the config
    const acctHost = acct.split('@').filter(Boolean)[1];
    const site = mastodonConfig.apiHost || acctHost;
    const acctDomain = mastodonConfig.acctDomain || acctHost;
    if (!(site && acctDomain)) {
        throw new ConfigNotFoundError('Mastodon RSS is disabled due to the lack of <a href="https://docs.rsshub.app/deploy/config#route-specific-configurations">relevant config</a>');
    }
    if (!config.feature.allow_user_supply_unsafe_domain && !allowSiteList.includes(site)) {
        throw new ConfigNotFoundError(`RSS for this domain is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true' or 'MASTODON_API_HOST' is set.`);
    }

    const search_url = `https://${site}/api/v2/search`;
    const cacheUid = `mastodon_acct_id/${site}/${acct}`;

    const account_id = await cache.tryGet(cacheUid, async () => {
        const search_response = await got({
            method: 'get',
            url: search_url,
            headers: apiHeaders(site),
            searchParams: {
                q: acct,
                type: 'accounts',
            },
        });
        const [acctUser, acctHost] = acct.split('@').filter(Boolean);
        let acctOnServer;

        if (acctHost) {
            acctOnServer = acctHost === acctDomain ? acctUser : acctUser + '@' + acctHost;
        } else {
            acctOnServer = acctUser;
        }

        const accountData = search_response.data.accounts.filter((item) => item.acct === acctOnServer);

        if (accountData.length === 0) {
            throw new Error(`acct ${acct} not found`);
        }
        return accountData[0].id;
    });
    return { site, account_id };
}

export default { apiHeaders, parseStatuses, getAccountStatuses, getAccountIdByAcct, allowSiteList };
