import Combine from "../Base/Combine"
import { FlowPanelFactory, FlowStep } from "../ImportFlow/FlowStep"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import { InputElement } from "../Input/InputElement"
import { SvgToPdf, SvgToPdfOptions } from "../../Utils/svgToPdf"
import { FixedInputElement } from "../Input/FixedInputElement"
import { FixedUiElement } from "../Base/FixedUiElement"
import FileSelectorButton from "../Input/FileSelectorButton"
import InputElementMap from "../Input/InputElementMap"
import { RadioButton } from "../Input/RadioButton"
import { Utils } from "../../Utils"
import { VariableUiElement } from "../Base/VariableUIElement"
import Loading from "../Base/Loading"
import BaseUIElement from "../BaseUIElement"
import Img from "../Base/Img"
import Title from "../Base/Title"
import { CheckBox } from "../Input/Checkboxes"
import Minimap from "../Base/Minimap"
import SearchAndGo from "./SearchAndGo"
import Toggle from "../Input/Toggle"
import List from "../Base/List"
import LeftIndex from "../Base/LeftIndex"
import Constants from "../../Models/Constants"
import Toggleable from "../Base/Toggleable"
import Lazy from "../Base/Lazy"
import LinkToWeblate from "../Base/LinkToWeblate"
import Link from "../Base/Link"
import { AllLanguagesSelector } from "../Popup/AllLanguagesSelector"

class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> {
    readonly IsValid: Store<boolean>
    readonly Value: Store<{ title: string; pages: string[] }>

    constructor() {
        const elements: InputElement<{ templateName: string; pages: string[] }>[] = []
        for (const templateName in SvgToPdf.templates) {
            const template = SvgToPdf.templates[templateName]
            elements.push(
                new FixedInputElement(
                    new Combine([
                        new FixedUiElement(templateName).SetClass("font-bold pr-2"),
                        template.description,
                    ]),
                    new UIEventSource({ templateName, pages: template.pages })
                )
            )
        }

        const file = new FileSelectorButton(
            new FixedUiElement("Select an svg image which acts as template"),
            {
                acceptType: "image/svg+xml",
                allowMultiple: true,
            }
        )
        const fileMapped = new InputElementMap<
            FileList,
            { templateName: string; pages: string[]; fromFile: true }
        >(
            file,
            (x0, x1) => x0 === x1,
            (filelist) => {
                if (filelist === undefined) {
                    return undefined
                }
                const pages = []
                let templateName: string = undefined
                for (const file of Array.from(filelist)) {
                    if (templateName == undefined) {
                        templateName = file.name.substring(file.name.lastIndexOf("/") + 1)
                        templateName = templateName.substring(0, templateName.lastIndexOf("."))
                    }
                    pages.push(file.text())
                }

                return {
                    templateName,
                    pages,
                    fromFile: true,
                }
            },
            (_) => undefined
        )
        elements.push(fileMapped)
        const radio = new RadioButton(elements, { selectFirstAsDefault: true })

        const loaded: Store<{ success: { title: string; pages: string[] } } | { error: any }> =
            radio.GetValue().bind((template) => {
                if (template === undefined) {
                    return undefined
                }
                if (template["fromFile"]) {
                    return UIEventSource.FromPromiseWithErr(
                        Promise.all(template.pages).then((pages) => ({
                            title: template.templateName,
                            pages,
                        }))
                    )
                }
                const urls = template.pages.map((p) => SelectTemplate.ToUrl(p))
                const dloadAll: Promise<{ title: string; pages: string[] }> = Promise.all(
                    urls.map((url) => Utils.download(url))
                ).then((pages) => ({
                    pages,
                    title: template.templateName,
                }))

                return UIEventSource.FromPromiseWithErr(dloadAll)
            })
        const preview = new VariableUiElement(
            loaded.map((pages) => {
                if (pages === undefined) {
                    return new Loading()
                }
                if (pages["error"] !== undefined) {
                    return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass(
                        "alert"
                    )
                }
                const svgs = pages["success"].pages
                if (svgs.length === 0) {
                    return new FixedUiElement("No pages loaded...").SetClass("alert")
                }
                const els: BaseUIElement[] = []
                for (const pageSrc of svgs) {
                    const el = new Img(pageSrc, true).SetClass("w-96 m-2 border-black border-2")
                    els.push(el)
                }
                return new Combine(els).SetClass("flex border border-subtle rounded-xl")
            })
        )

        super([new Title("Select template"), radio, new Title("Preview"), preview])
        this.Value = loaded.map((l) => (l === undefined ? undefined : l["success"]))
        this.IsValid = this.Value.map((v) => v !== undefined)
    }

    public static ToUrl(spec: string) {
        if (spec.startsWith("http")) {
            return spec
        }
        let path = window.location.pathname
        path = path.substring(0, path.lastIndexOf("/"))
        return window.location.protocol + "//" + window.location.host + path + "/" + spec
    }
}

class SelectPdfOptions
    extends Combine
    implements FlowStep<{ title: string; pages: string[]; options: SvgToPdfOptions }>
{
    readonly IsValid: Store<boolean>
    readonly Value: Store<{ title: string; pages: string[]; options: SvgToPdfOptions }>

    constructor(title: string, pages: string[], getFreeDiv: () => string) {
        const dummy = new CheckBox("Don't add data to the map (to quickly preview the PDF)", false)
        const overrideMapLocation = new CheckBox(
            "Override map location: use a selected location instead of the location set in the template",
            false
        )
        const locationInput = Minimap.createMiniMap().SetClass("block w-full")
        const searchField = new SearchAndGo({ leafletMap: locationInput.leafletMap })
        const selectLocation = new Combine([
            new Toggle(
                new Combine([new Title("Select override location"), searchField]).SetClass("flex"),
                undefined,
                overrideMapLocation.GetValue()
            ),
            new Toggle(
                locationInput.SetStyle("height: 20rem"),
                undefined,
                overrideMapLocation.GetValue()
            ).SetStyle("height: 20rem"),
        ])
            .SetClass("block")
            .SetStyle("height: 25rem")
        super([new Title("Select options"), dummy, overrideMapLocation, selectLocation])
        this.Value = dummy.GetValue().map(
            (disableMaps) => {
                return {
                    pages,
                    title,
                    options: <SvgToPdfOptions>{
                        disableMaps,
                        getFreeDiv,
                        overrideLocation: overrideMapLocation.GetValue().data
                            ? locationInput.location.data
                            : undefined,
                    },
                }
            },
            [overrideMapLocation.GetValue(), locationInput.location]
        )
        this.IsValid = new ImmutableStore(true)
    }
}

class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }> {
    readonly IsValid: Store<boolean>
    readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>

    constructor(title: string, pages: string[], options: SvgToPdfOptions) {
        const svgToPdf = new SvgToPdf(title, pages, options)
        const languageSelector = new AllLanguagesSelector()
        const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare())

        super([
            new Title("Select languages..."),
            languageSelector,
            new Toggle(
                new Loading("Preparing maps..."),
                undefined,
                isPrepared.map((p) => p === undefined)
            ),
        ])
        this.Value = isPrepared.map(
            (isPrepped) => {
                if (isPrepped === undefined) {
                    return undefined
                }
                if (isPrepped["success"] !== undefined) {
                    const svgToPdf = isPrepped["success"]
                    const langs = languageSelector.GetValue().data
                    console.log("Languages are", langs)
                    if (langs.length === 0) {
                        return undefined
                    }
                    return { svgToPdf, languages: langs }
                }
                return undefined
            },
            [languageSelector.GetValue()]
        )
        this.IsValid = this.Value.map((v) => v !== undefined)
    }
}

class InspectStrings
    extends Toggle
    implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }>
{
    readonly IsValid: Store<boolean>
    readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>

    constructor(svgToPdf: SvgToPdf, languages: string[]) {
        const didLoadLanguages = UIEventSource.FromPromiseWithErr(
            svgToPdf.PrepareLanguages(languages)
        ).map((l) => l !== undefined && l["success"] !== undefined)

        super(
            new Combine([
                new Title("Inspect translation strings"),
                ...languages.map(
                    (l) => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l))
                ),
            ]),
            new Loading(),
            didLoadLanguages
        )
        this.Value = new ImmutableStore({ svgToPdf, languages })
        this.IsValid = didLoadLanguages
    }

    private static createOverviewPanel(svgToPdf: SvgToPdf, language: string): BaseUIElement {
        const elements: BaseUIElement[] = []
        let foundTranslations = 0
        const allKeys = Array.from(svgToPdf.translationKeys())
        for (const translationKey of allKeys) {
            let spec = translationKey
            if (translationKey.startsWith("layer.")) {
                spec = "layers:" + translationKey.substring(6)
            } else {
                spec = "core:" + translationKey
            }
            const translated = svgToPdf.getTranslation("$" + translationKey, language, true)
            if (translated) {
                foundTranslations++
            }
            const linkToWeblate = new Link(
                spec,
                LinkToWeblate.hrefToWeblate(language, spec),
                true
            ).SetClass("font-bold link-underline")
            elements.push(
                new Combine([
                    linkToWeblate,
                    "&nbsp;",
                    translated ?? new FixedUiElement("No translation found!").SetClass("alert"),
                ])
            )
        }

        return new Toggleable(
            new Title("Translations for " + language),
            new Combine([
                `${foundTranslations}/${allKeys.length} of translations are found (${Math.floor(
                    (100 * foundTranslations) / allKeys.length
                )}%)`,
                "The following keys are used:",
                new List(elements),
            ]),
            { closeOnClick: false, height: "15rem" }
        )
    }
}

class SavePdf extends Combine {
    constructor(svgToPdf: SvgToPdf, languages: string[]) {
        super([
            new Title("Generating your pdfs..."),
            new List(
                languages.map(
                    (lng) =>
                        new Toggle(
                            lng + " is done!",
                            new Loading("Creating pdf for " + lng),
                            UIEventSource.FromPromiseWithErr(
                                svgToPdf.ConvertSvg(lng).then(() => true)
                            ).map((x) => x !== undefined && x["success"] === true)
                        )
                )
            ),
        ])
    }
}

export class PdfExportGui extends LeftIndex {
    constructor(freeDivId: string) {
        let i = 0
        const createDiv = (): string => {
            const div = document.createElement("div")
            div.id = "freediv-" + i++
            document.getElementById(freeDivId).append(div)
            return div.id
        }

        Constants.defaultOverpassUrls.splice(0, 1)
        const { flow, furthestStep, titles } = FlowPanelFactory.start(
            new Title("Select template"),
            new SelectTemplate()
        )
            .then(
                new Title("Select options"),
                ({ title, pages }) => new SelectPdfOptions(title, pages, createDiv)
            )
            .then(
                "Generate maps...",
                ({ title, pages, options }) => new PreparePdf(title, pages, options)
            )
            .then(
                "Inspect translations",
                ({ svgToPdf, languages }) => new InspectStrings(svgToPdf, languages)
            )
            .finish("Generating...", ({ svgToPdf, languages }) => new SavePdf(svgToPdf, languages))

        const toc = new List(
            titles.map(
                (title, i) =>
                    new VariableUiElement(
                        furthestStep.map((currentStep) => {
                            if (i > currentStep) {
                                return new Combine([title]).SetClass("subtle")
                            }
                            if (i == currentStep) {
                                return new Combine([title]).SetClass("font-bold")
                            }
                            if (i < currentStep) {
                                return title
                            }
                        })
                    )
            ),
            true
        )

        const leftContents: BaseUIElement[] = [toc].map((el) => el?.SetClass("pl-4"))

        super(leftContents, flow)
    }
}