/* eslint eqeqeq: 0, curly: "error", "no-extra-parens": "off"  */
/* global API,UI,PluginHandler,ko,ace */
const self = this;

this.plugin = {
    FMFastModeBasePath: "",

    PreInit: function () {
        //Called prior to the plugins initialisation, before the tabs are loaded.
        //This method must not invoke any module/plugin specific API calls.
    },

    PostInit: function () {
        ko.applyBindings(storeHomeVM, document.getElementById("tab_StorePlugin_Store"));
    },

    AMPDataLoaded: function () {
        
    },

    Reset: function () {
        
    }
};

this.tabs = [
    {
        File: "Store.html",
        ExternalTab: false,
        ShortName: "Store",
        Name: "Store (Beta)",
        Icon: "shopping_bag",
        RequiredPermission: "Store.Store.BrowseStore",
        Click: initStore,
        BodyClass: "noPaddingTab",
        Order: 10,
        PopHandler: handlePopstate,
    },
];

this.stylesheet = "Stylesheet.css";    //Styles for tab-specific styles

this.features = {};

const storeHomeVM = new StoreHomeVM();

async function initStore() {
    storeHomeVM.refresh();
};

function StoreHomeVM() {
    const self = this;
    this.Stores = ko.observableArray([]);
    this.SelectedStore = ko.observable(null);
    this.AppName = ko.pureComputed(() => viewModels.support?.appName() || "this game");
    this.refresh = async function () {
        const storeData = await API.StorePlugin.GetAvailableStoresAsync();
        self.Stores.removeAll();
        const storeVMs = ko.quickmap.to(StoreVM, storeData, false, { vm: self });
        ko.utils.arrayPushAll(self.Stores, storeVMs);
        if (self.Stores().length === 1) {
            self.Stores()[0].Click();
        }
    };
}

function StoreVM() {
    const self = this;
    this.vm = null; //of StoreHomeVM
    this.Id = ko.observable("");
    this.Name = ko.observable("");
    this.SanitisedName = ko.pureComputed(() => this.Name().toLowerCase().replace(/[^a-z0-9]+/g, '-'));
    this.Description = ko.observable("");
    this.LogoUrl = ko.observable("");
    this.BrandColor1 = ko.observable("");
    this.BrandColor2 = ko.observable("");
    this.BrandColor3 = ko.observable("");
    this.Unavailable = ko.observable(false);
    this.BrandGradient = ko.pureComputed(() => this.Unavailable() ? "linear-gradient(to top right, #666, #222)" : `linear-gradient(to top right, ${this.BrandColor1()}, ${this.BrandColor2()})`);
    this.Categories = ko.observableArray([]);
    this.SelectedCategory = ko.observable(null); //of StoreCategoryVM
    this.Selected = ko.observable(false);
    this.loaded = ko.observable(false);

    this.searchVM = new StoreCategoryVM();
    this.searchVM.store = this;
    this.searchVM.IsSearch = true;
    this.searchVM.IconName = "search";
    this.searchVM.Name = ko.pureComputed(() => `Search ${this.Name()}`);

    this.SearchTerm = ko.observable("");

    this.GoBack = function () {
        this.Selected(false);
        this.vm.SelectedStore(null);
        UI.NavigateTo("/store");
    }

    this.Click = function (noNav) {
        if (this.Unavailable()) { return; }
        this.vm.SelectedStore()?.Selected(false);
        this.vm.SelectedStore(this);
        if (!this.loaded()) {
            this.refresh();
        }
        if (noNav !== true) {
            UI.NavigateTo("/store/" + this.SanitisedName());
        }
    };

    this.refresh = async function () {
        const storeData = await API.StorePlugin.GetStoreCategoriesAsync(this.Id());
        this.Categories.removeAll();
        const categoryVMs = ko.quickmap.to(StoreCategoryVM, storeData, false, { store: self });
        ko.utils.arrayPushAll(this.Categories, categoryVMs);
        this.loaded(true);
    };

    this.SearchStore = async function () {
        this.SelectedCategory()?.Selected(false);
        this.searchVM.Description = ko.observable(`Search results for "${this.SearchTerm()}"`);
        this.searchVM.SearchTerm(this.SearchTerm());
        this.SelectedCategory(this.searchVM);
        await this.searchVM.refresh();
    };
};

function StoreCategoryVM() {
    const self = this;
    this.store = null; //of StoreVM
    this.Id = ko.observable("");
    this.Name = ko.observable("");
    this.SanitisedName = ko.pureComputed(() => this.Name().toLowerCase().replace(/[^a-z0-9]+/g, '-'));
    this.Description = ko.observable("");
    this.IconUrl = ko.observable("");
    this.IconName = ko.observable("");
    this.Selected = ko.observable(false);

    this.loaded = ko.observable(false);
    this.Entries = ko.observableArray([]); //of StoreEntryVM
    this.CurrentPage = ko.observable(0);
    this.PageCount = ko.observable(0);
    this.PageSize = ko.observable(20);

    this.IsSearch = false;
    this.SearchTerm = ko.observable("");
    this.Busy = false;

    this.MorePagesAvailable = ko.pureComputed(() => this.CurrentPage() < this.PageCount() - 1);

    this.GoBack = function () {
        self.Selected(false);
        self.store.SelectedCategory(null);
    }

    this.Click = async function (noNav) {
        self.store.SelectedCategory()?.Selected(false);
        self.store.SelectedCategory(this);
        if (noNav !== true) {
            UI.NavigateTo(`/store/${self.store.SanitisedName()}/${this.SanitisedName()}`);
        }
        await this.Reset();
    };

    this.Reset = async function () {
        self.CurrentPage(0);
        self.PageCount(0);
        await this.refresh();
    };

    this.refresh = async function () {
        if (this.Busy) { return; }

        this.Busy = true;
        let entries = [];

        if (self.IsSearch) {
            entries = await API.StorePlugin.SearchStoreAsync(self.store.Id(), self.SearchTerm(), self.CurrentPage(), self.PageSize());
        }
        else {
            entries = await API.StorePlugin.GetStoreEntriesForCategoryAsync(this.store.Id(), this.Id(), this.CurrentPage(), this.PageSize());
        }

        if (this.CurrentPage() == 0) {
            this.Entries.removeAll();
        }
        
        const entryVMs = ko.quickmap.to(StoreEntryVM, entries.Records, false, { category: self });
        ko.utils.arrayPushAll(this.Entries, entryVMs);
        this.PageCount(entries.TotalPages);
        this.CurrentPage(entries.PageIndex);
        this.loaded(true);

        this.Busy = false;
    };

    this.advancePage = async function () {
        if (self.Busy) { return; }

        if (self.CurrentPage() >= self.PageCount() - 1) {
            return; //Already on last page
        }

        self.CurrentPage(self.CurrentPage() + 1);

        await self.refresh();
    };
};

function StoreEntryVM() {
    this.category = null; //of StoreCategoryVM
    this.Busy = ko.observable(false);
    this.Id = ko.observable("");
    this.Slug = ko.observable("");
    this.Name = ko.observable("");
    this.Description = ko.observable("");
    this.Version = ko.observable("");
    this.AuthorName = ko.observable("");
    this.AuthorId = ko.observable("");
    this.Tags = ko.observableArray([]);
    this.Categories = ko.observableArray([]);
    this.Dependencies = ko.observableArray([]);
    this.Provides = ko.observableArray([]);
    this.DownloadCount = ko.observable(0);
    this.DownloadUrl = ko.observable("");
    this.DownloadCountFormatted = ko.computed(() => {
        const count = this.DownloadCount();
        if (count >= 1000000) {
            return `${(count / 1000000).toFixed(1)}M`;
        } else if (count >= 1000) {
            return `${(count / 1000).toFixed(1)}K`;
        }
        return count;
    });
    this.SourceUrl = ko.observable("");
    this.IconUrl = ko.observable("");
    this.DiscussionUrl = ko.observable("");
    this.ScreenshotUrls = ko.observableArray([]);
    this.RatingStars = ko.observable(null);
    this.RatingStarsFormatted = ko.computed(() => {
        const stars = this.RatingStars();
        if (stars === null) {
            return "";
        }
        if (stars < 0 || stars > 5) { return ""; }
        const fullStars = Math.floor(stars);
        const halfStar = stars % 1 >= 0.5 ? "★" : "";
        return "★".repeat(fullStars) + halfStar + "☆".repeat(5 - fullStars - (halfStar ? 1 : 0));
    });
    this.RatingCount = ko.observable(null);
    this.RatingCountFormatted = ko.computed(() => {
        const count = this.RatingCount();
        if (count >= 1000000) {
            return `${(count / 1000000).toFixed(1)}M`;
        } else if (count >= 1000) {
            return `${(count / 1000).toFixed(1)}K`;
        }
        return count;
    });
    this.SizeBytes = ko.observable(0);
    this.SizeBytesFormatted = ko.computed(() => {
        const size = this.SizeBytes();
        if (size === 0) { return ""; }
        if (size >= 1000000000) {
            return `${(size / 1000000000).toFixed(1)} GB`;
        } else if (size >= 1000000) {
            return `${(size / 1000000).toFixed(1)} MB`;
        } else if (size >= 1000) {
            return `${(size / 1000).toFixed(1)} KB`;
        }
        return `< 1 KB`;
    });
    this.IsInstalled = ko.observable(false);
    this.InstalledVersion = ko.observable("");
    this.InstalledPath = ko.observable("");
    this.InstalledDate = ko.observable(null);
    this.Disabled = ko.observable(false);
    this.UpdateAvailable = ko.pureComputed(() => this.IsInstalled() && this.Version() !== this.InstalledVersion());
    this.AutoUpdate = ko.observable(false);
    this.RequiresManualInstall = ko.observable(false);
    this.RequiresPayment = ko.observable(false);
    this.PaymentCurrency = ko.observable("");
    this.PaymentAmount = ko.observable(0.0);
    this.Selected = ko.observable(false);
    this.loaded = ko.observable(false);
    this.Click = async function () {
        if (!this.loaded()) {
            await this.refresh();
        }
    };
    this.refresh = async function () {
        const entryData = await API.StorePlugin.GetStoreEntryAsync(this.category.store.Id(), this.Id());
        ko.quickmap.map(this, entryData);
        this.loaded(true);
    };
    this.Install = async function () {
        if (this.IsInstalled()) {
            return;
        }

        if (this.RequiresManualInstall()) {
            await UI.ShowModalAsync("Manual Installation Required", "This plugin cannot be installed automatically. Please download this plugin manually and upload it into your instance.", UI.Icons.Exclamation, UI.OKActionOnly);
            return;
        }

        this.Busy(true);
        const installTask = await API.StorePlugin.InstallStoreEntryTaskAsync(this.category.store.Id(), this.Id());
        await installTask.waitCompleteAsync();        
        await this.refresh();
        this.Busy(false);
    };
    this.Uninstall = async function () {
        if (!this.IsInstalled()) {
            return;
        }
        await API.StorePlugin.UninstallStoreEntryAsync(this.category.store.Id(), this.Id());
    };
    this.Update = async function () {
        if (!this.IsInstalled()) {
            return;
        }
        await API.StorePlugin.UpdateStoreEntryAsync(this.category.store.Id(), this.Id());
    };
    this.ToggleDisabled = async function () {
        if (!this.IsInstalled()) {
            return;
        }
        await API.StorePlugin.SetStoreEntryDisabled(this.category.store.Id(), this.Id(), !this.Disabled());
        this.Disabled(!this.Disabled());
    };
    this.ToggleAutoUpdate = async function () {
        if (!this.IsInstalled()) {
            return;
        }
        await API.StorePlugin.SetStoreEntryAutoUpdate(this.category.store.Id(), this.Id(), !this.AutoUpdate());
        this.AutoUpdate(!this.AutoUpdate());
    };
};

async function handlePopstate(_, segments) {
    if (segments.length < 1 || segments[0] !== "store") { return; }

    if (segments.length == 1) {
        storeHomeVM.SelectedStore(null);
        return;
    }
    
    const storeName = segments[1];
    const store = storeHomeVM.Stores().find(s => s.SanitisedName() === storeName);
    if (!store) {
        storeHomeVM.SelectedStore()?.Selected(false);
        storeHomeVM.SelectedStore(null);
        return;
    }

    await store.Click(true);
    if (segments.length == 2) {
        store.SelectedCategory(null);
        return;
    }

    const categoryName = segments[2];
    const category = store.Categories().find(c => c.SanitisedName() === categoryName);
    if (category == null) {
        return;
    }

    await category.Click(true);
};

