import { isPlayObject } from "../../../../core/Atomic.ts";
import { getRoot } from "../../../ioc.ts";
import { isDebugMode } from "../../../utils.ts";
import { urlContainsKnownMediaDomain } from "../../../utils/RequestUtils.ts";
import type { MSCache } from "../../Cache.ts";
import { isSuperAgentResponseError } from "../../errors/ErrorUtils.ts";
import type {AbstractApiOptions, SourceData} from "../../infrastructure/Atomic.ts";
import { type ActivityAssets, ARTWORK_PLACEHOLDER, type DiscordStrongData, MS_ART } from "../../infrastructure/config/client/discord.ts";
import AbstractApiClient from "../AbstractApiClient.ts";
import type { CoverArtApiClient } from "../musicbrainz/CoverArtApiClient.ts";
import EventEmitter from "events";
import request from 'superagent';


export class DiscordAbstractClient extends AbstractApiClient {

    declare config: DiscordStrongData;

    emitter: EventEmitter;
    cache: MSCache;
    covertArtApi: CoverArtApiClient;
    artFail: boolean = false;
    artFailCount = 0;

    constructor(type: string, name: any, config: DiscordStrongData, options: AbstractApiOptions) {
        super(type, name, config, options);
        this.emitter = new EventEmitter();
        this.cache = getRoot().items.cache();
        this.covertArtApi = getRoot().items.coverArtApi;
    }

    getArtworkUrl = async (artUrl: string): Promise<string | undefined> => {

        if (this.config.applicationId === undefined) {
            return;
        }
        if(this.artFail) {
            return;
        }

        const cachedUrl = await this.cache.cacheMetadata.get<string>(`${this.config.applicationId}-${artUrl}`);
        if (cachedUrl !== undefined) {
            return cachedUrl;
        }

        try {
            const imgResp = await request.post(`https://discord.com/api/v10/applications/${this.config.applicationId}/external-assets`)
                .set('Authorization', this.config.token)
                .type('json')
                .send({ "urls": [artUrl] });
            this.artFailCount = 0;
            const proxied = `mp:${imgResp.body[0].external_asset_path}`
            await this.cache.cacheMetadata.set(artUrl, proxied);
            return proxied;
        } catch (e) {
            this.artFailCount++;
            this.logger.warn(new Error('Failed to upload art url', { cause: e }));
            if (isSuperAgentResponseError(e)) {
                if (e.status === 401 || e.status === 403) {
                    this.artFail = true;
                }
            } else if (this.artFailCount > 3) {
                this.logger.verbose('More than 3 consecutive failures to upload art...turning off to stop spamming bad requests');
                this.artFail = true;
            }
            return;
        }
    }

    getArtAsset = async (data: SourceData, artUrl?: string, useMediaProxy: boolean = true): Promise<ActivityAssets | undefined> => {
        const {
            artwork = false
        } = this.config;
        const {
            artworkDefaultUrl = ARTWORK_PLACEHOLDER,
            applicationId
        } = this.config;

        const assets: ActivityAssets = {};

        if(applicationId !== undefined) {

            let art = artworkDefaultUrl;
            if(artUrl !== undefined) {
                if(urlContainsKnownMediaDomain(artUrl)) {
                    art = artUrl;
                } else if (artwork !== false) {
                    if (Array.isArray(artwork)) {
                        const allowed = artwork.some(x => artUrl.toLocaleLowerCase().includes(x.toLocaleLowerCase()));
                        if (allowed) {
                            art = artUrl;
                        } else if(isDebugMode()) {
                            this.logger.debug(`${artUrl} does not contain any allowed domains`);
                        }
                    } else {
                        art = artUrl;
                    }

                    const u = new URL(art);
                    // only allow secure protocol as this is likely to be a real domain that is public accessible
                    // IP domain usually uses http only
                    if (u.protocol !== 'https:') {
                        if(isDebugMode()) {
                            this.logger.debug(`${artUrl} is not https`);
                        }
                        art = artworkDefaultUrl;
                    }
                }
            }

            if(art === artworkDefaultUrl) {
                const play = isPlayObject(data) ? data : data.play;
                if(play.data.meta?.brainz?.album !== undefined || play.data.meta?.brainz?.releaseGroup !== undefined) {
                    let usedCAA = false;
                    if(play.data.meta?.brainz?.album !== undefined) {
                        const albumArt = await this.covertArtApi.getCoverThumb(play.data.meta?.brainz?.album, 'release', {size: 250});
                        if(albumArt !== undefined) {
                            art = albumArt;
                            usedCAA = true;
                        }
                    }
                    if(!usedCAA && play.data.meta?.brainz?.releaseGroup !== undefined) {
                        const albumArt = await this.covertArtApi.getCoverThumb(play.data.meta?.brainz?.releaseGroup, 'release-group', {size: 250});
                        if(albumArt !== undefined) {
                            art = albumArt;
                            usedCAA = true;
                        }
                    }
                } else {
                    this.logger.trace('No brainz Release MBID to use for cover art archive');
                }
            }

            // https://docs.discord.com/developers/events/gateway-events#activity-object-activity-assets
            // https://docs.discord.com/developers/events/gateway-events#activity-object-activity-asset-image
            const usedUrl = useMediaProxy ? await this.getArtworkUrl(art) : art;
            if(usedUrl !== undefined) {
                assets.largeImage = usedUrl;
            }
            if(art !== MS_ART) {
                const smallArt = useMediaProxy ? await this.getArtworkUrl(MS_ART) : MS_ART;
                if(smallArt !== undefined) {
                        assets.smallImage = smallArt;
                        assets.smallText = 'Via Multi-Scrobbler'
                        assets.smallUrl = 'https://multi-scrobbler.app'
                } 
            }
        }

        if(Object.keys(assets).length === 0) {
            return;
        }
        return assets;
    }

}