import { T1 } from "../../Types/Items";
import { RPC } from "rpclibrary";
import { Inject, Module } from "../../Injector/ServiceDecorator";
import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
import { FrontworkComponent } from "../../Types/FrontworkComponent";
import { TableDefinitionExporter } from "../../Types/Interfaces";
import { FrontworkAdmin } from "../../Admin/Admin";
import { TableDefiniton } from "../../Types/Types";
import { IAdmin } from "../../Admin/Interface";

const fetch = require('node-fetch')
const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */);

export type Item = {
    id?:number
    name:string
    iconname:string
    url:string
    quality:string
    hidden:boolean
}

@Module()
export class ItemManager
implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter{
    name = "ItemManager" as "ItemManager";    
    
    @Inject(IAdmin)
    private admin: FrontworkAdmin

    exportRPCs(): RPC<any, any>[]{
        return [{
            name: 'getItems',
            call: this.getItems
        },{
            name: 'getItem',
            call: this.getItem
        }]
    }

    exportRPCFeatures() {
        return []
    }

    getItems = async () :Promise<Item[]> => await this.admin.knex.select('*').from('items')

    getItem  = async (name:string):Promise<Item> => {
        const res = await fetch('https://classic.wowhead.com/item='+name+'&xml'); 
        const txt = await res.text(); 
        const r = await parser.parseStringPromise(txt); 

        try{
            return <Item>{
                name: r.wowhead.item[0].name[0],
                iconname: r.wowhead.item[0].icon[0]._,
                url: r.wowhead.item[0].link[0],
                quality: r.wowhead.item[0].quality[0]._,
            } 
        }catch (e){
            console.log(name)
            throw e
        }
    }

    getTableDefinitions(): TableDefiniton[] {
        return [
            {
                name: 'reservations',
                tableBuilder: (table) => {
                    table.integer("user_id").primary()
                    table.foreign("user_id").references("id").inTable('users')
                    table.integer("item_id").primary()
                    table.foreign("item_id").references("id").inTable('items')
                    table.integer("raid_id").primary()
                    table.foreign("raid_id").references("id").inTable('raids')
                }
            },{
                name: 'items',
                tableBuilder: (table) => {
                    table.increments("id").primary()
                    table.string('name').unique().notNullable()
                    table.string('iconname').notNullable()
                    table.string('url').notNullable()
                    table.string('quality').defaultTo('Epic').notNullable()
                    table.boolean('hidden').defaultTo(false).notNullable()
                }
            }]
    }

    countItems = async() :Promise<number> => {
        const count = await this.admin.knex('items').count('*');
        return <number>count[0]['count(*)']
    }

    private initialized = false
    initialize = async () => {
        if(!this.initialized)
            this.initialized = true        
        
        const allItems = [...T1]
        const countCache = await this.countItems()
        if(countCache != allItems.length){
            const items:Item[] = await Promise.all(allItems.map((i) => this.getItem(i)))
            try{
                await this.admin
                .knex('items')
                .insert(items)
            }catch(e){
                console.log(e)
                console.info("Skipping item insertion")
            }
        }
    }
}