import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { OsmConnection } from "../Osm/OsmConnection"
import { MangroveIdentity } from "../Web/MangroveReviews"
import { Store, UIEventSource } from "../UIEventSource"
import { QueryParameters } from "../Web/QueryParameters"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { Utils } from "../../Utils"
import Locale from "../../UI/i18n/Locale"
import ElementsState from "./ElementsState"
import SelectedElementTagsUpdater from "../Actors/SelectedElementTagsUpdater"
import { Changes } from "../Osm/Changes"
import ChangeToElementsActor from "../Actors/ChangeToElementsActor"
import PendingChangesUploader from "../Actors/PendingChangesUploader"
import * as translators from "../../assets/translators.json"
import Maproulette from "../Maproulette"

/**
 * The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
 * which layers they enabled, ...
 */
export default class UserRelatedState extends ElementsState {
    /**
     The user credentials
     */
    public osmConnection: OsmConnection
    /**
     THe change handler
     */
    public changes: Changes
    /**
     * The key for mangrove
     */
    public mangroveIdentity: MangroveIdentity

    /**
     * Maproulette connection
     */
    public maprouletteConnection: Maproulette

    public readonly isTranslator: Store<boolean>

    public readonly installedUserThemes: Store<string[]>

    constructor(layoutToUse: LayoutConfig, options?: { attemptLogin: true | boolean }) {
        super(layoutToUse)

        this.osmConnection = new OsmConnection({
            dryRun: this.featureSwitchIsTesting,
            fakeUser: this.featureSwitchFakeUser.data,
            oauth_token: QueryParameters.GetQueryParameter(
                "oauth_token",
                undefined,
                "Used to complete the login"
            ),
            osmConfiguration: <"osm" | "osm-test">this.featureSwitchApiURL.data,
            attemptLogin: options?.attemptLogin,
        })
        const translationMode = this.osmConnection.GetPreference("translation-mode").sync(
            (str) => (str === undefined ? undefined : str === "true"),
            [],
            (b) => (b === undefined ? undefined : b + "")
        )

        translationMode.syncWith(Locale.showLinkToWeblate)

        this.isTranslator = this.osmConnection.userDetails.map((ud) => {
            if (!ud.loggedIn) {
                return false
            }
            const name = ud.name.toLowerCase().replace(/\s+/g, "")
            return translators.contributors.some(
                (c) => c.contributor.toLowerCase().replace(/\s+/g, "") === name
            )
        })

        this.isTranslator.addCallbackAndRunD((ud) => {
            if (ud) {
                Locale.showLinkToWeblate.setData(true)
            }
        })

        this.changes = new Changes(this, layoutToUse?.isLeftRightSensitive() ?? false)

        new ChangeToElementsActor(this.changes, this.allElements)
        new PendingChangesUploader(this.changes, this.selectedElement)

        this.mangroveIdentity = new MangroveIdentity(
            this.osmConnection.GetLongPreference("identity", "mangrove")
        )

        this.maprouletteConnection = new Maproulette()

        if (layoutToUse?.hideFromOverview) {
            this.osmConnection.isLoggedIn.addCallbackAndRunD((loggedIn) => {
                if (loggedIn) {
                    this.osmConnection
                        .GetPreference("hidden-theme-" + layoutToUse?.id + "-enabled")
                        .setData("true")
                    return true
                }
            })
        }

        if (this.layoutToUse !== undefined && !this.layoutToUse.official) {
            console.log("Marking unofficial theme as visited")
            this.osmConnection.GetLongPreference("unofficial-theme-" + this.layoutToUse.id).setData(
                JSON.stringify({
                    id: this.layoutToUse.id,
                    icon: this.layoutToUse.icon,
                    title: this.layoutToUse.title.translations,
                    shortDescription: this.layoutToUse.shortDescription.translations,
                    definition: this.layoutToUse["definition"],
                })
            )
        }

        this.InitializeLanguage()
        new SelectedElementTagsUpdater(this)
        this.installedUserThemes = this.InitInstalledUserThemes()
    }

    private InitializeLanguage() {
        const layoutToUse = this.layoutToUse
        Locale.language.syncWith(this.osmConnection.GetPreference("language"))
        Locale.language.addCallback((currentLanguage) => {
            if (layoutToUse === undefined) {
                return
            }
            if (Locale.showLinkToWeblate.data) {
                return true // Disable auto switching as we are in translators mode
            }
            if (this.layoutToUse.language.indexOf(currentLanguage) < 0) {
                console.log(
                    "Resetting language to",
                    layoutToUse.language[0],
                    "as",
                    currentLanguage,
                    " is unsupported"
                )
                // The current language is not supported -> switch to a supported one
                Locale.language.setData(layoutToUse.language[0])
            }
        })
        Locale.language.ping()
    }

    private InitInstalledUserThemes(): Store<string[]> {
        const prefix = "mapcomplete-unofficial-theme-"
        const postfix = "-combined-length"
        return this.osmConnection.preferencesHandler.preferences.map((prefs) =>
            Object.keys(prefs)
                .filter((k) => k.startsWith(prefix) && k.endsWith(postfix))
                .map((k) => k.substring(prefix.length, k.length - postfix.length))
        )
    }

    public GetUnofficialTheme(id: string):
        | {
              id: string
              icon: string
              title: any
              shortDescription: any
              definition?: any
              isOfficial: boolean
          }
        | undefined {
        console.log("GETTING UNOFFICIAL THEME")
        const pref = this.osmConnection.GetLongPreference("unofficial-theme-" + id)
        const str = pref.data

        if (str === undefined || str === "undefined" || str === "") {
            pref.setData(null)
            return undefined
        }

        try {
            const value: {
                id: string
                icon: string
                title: any
                shortDescription: any
                definition?: any
                isOfficial: boolean
            } = JSON.parse(str)
            value.isOfficial = false
            return value
        } catch (e) {
            console.warn(
                "Removing theme " +
                    id +
                    " as it could not be parsed from the preferences; the content is:",
                str
            )
            pref.setData(null)
            return undefined
        }
    }
}