import { Store, Stores } from "../UIEventSource"
import BaseUIElement from "../../UI/BaseUIElement"
import { LicenseInfo } from "./LicenseInfo"
import { Utils } from "../../Utils"
import { Feature, Point } from "geojson"

export interface ProvidedImage {
    url: string
    url_hd?: string
    key: string
    provider: ImageProvider
    id: string
    /**
     * An alternative ID, used to deduplicate some images
     */
    alt_id?: string
    date?: Date
    status?: string | "ready"
    /**
     * Compass angle of the taken image
     * 0 = north, 90° = East
     */
    rotation?: number
    lat?: number
    lon?: number
    host?: string
    isSpherical: boolean
    license?: LicenseInfo
    originalAttribute?: {key: string, value: string}
}

export interface PanoramaView {
    url: string
    /**
     * 0 - 359
     * Degrees in which the picture is taken, with north = 0; going clockwise
     */
    northOffset?: number
    pitchOffset?: number
    provider: ImageProvider | string
}

/**
 * The property of "nearbyFeatures" in ImagePreview.svelte.
 * These properties declare how they are rendered
 */
export interface HotspotProperties {
    /**
     * The popup text when hovering
     */
    name: string

    /**
     * If true: the panorama view will automatically turn towards this object
     */
    focus: boolean
    /**
     * The pitch degrees to display this.
     * If "auto": will determine the pitch automatically based on distance
     */
    pitch: number | "auto"

    gotoPanorama: Feature<Point, PanoramaView>
}

export default abstract class ImageProvider {
    public abstract readonly defaultKeyPrefixes: string[]

    public abstract readonly name: string

    public abstract SourceIcon(
        img?: { id: string; url: string; host?: string },
        location?: { lon: number; lat: number }
    ): BaseUIElement

    /**
     * Gets all the relevant URLS for the given tags and for the given prefixes;
     * extracts the necessary information
     * @param tags
     * @param prefixes
     */
    public async getRelevantUrlsFor(
        tags: Record<string, string>,
        prefixes: string[]
    ): Promise<ProvidedImage[]> {
        const relevantUrls: ProvidedImage[] = []
        const seenValues = new Set<string>()

        for (const key in tags) {
            if (
                !prefixes.some(
                    (prefix) => key === prefix || key.match(new RegExp(prefix + ":[0-9]+"))
                )
            ) {
                continue
            }
            const values = Utils.NoEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
            for (const value of values) {
                if (seenValues.has(value)) {
                    continue
                }
                seenValues.add(value)
                let images = this.ExtractUrls(key, value)
                if (!Array.isArray(images)) {
                    images = await images
                }
                if (images) {
                    relevantUrls.push(...images)
                }
            }
        }
        return relevantUrls
    }

    public getRelevantUrls(
        tags: Record<string, string>,
        prefixes: string[]
    ): Store<ProvidedImage[]> {
        return Stores.FromPromise(this.getRelevantUrlsFor(tags, prefixes))
    }

    public abstract ExtractUrls(
        key: string,
        value: string
    ): undefined | ProvidedImage[] | Promise<ProvidedImage[]>

    public abstract DownloadAttribution(providedImage: {
        id: string
    }): Promise<LicenseInfo>

    public abstract apiUrls(): string[]

    /**
     * URL to visit the image on the original website
     */
    public abstract visitUrl(
        image: Partial<ProvidedImage>,
        location?: { lon: number; lat: number }
    ): string | undefined

    public abstract getPanoramaInfo(image: {
        id: string
    }): Promise<Feature<Point, PanoramaView>> | undefined

    public static async offerImageAsDownload(image: { url_hd?: string, url: string }) {
        const response = await fetch(image.url_hd ?? image.url)
        const blob = await response.blob()
        Utils.offerContentsAsDownloadableFile(blob, new URL(image.url).pathname.split("/").at(-1), {
            mimetype: "image/jpg",
        })
    }
}
