import { Component } from '../../lib/vues6/vues6.js'
import { utility } from '../service/Utility.service.js'
import { location_service } from '../service/Location.service.js'
import { resource_service } from '../service/Resource.service.js'
import { action_service } from '../service/Action.service.js'

const template = `
<div class="card col-12 col-sm-10 offset-sm-1 col-md-8 offset-md-2 col-xl-6 offset-xl-3 mb-5">
    <span v-if="!can_access">
        <div class="row m-5">
            <div class="col-12 text-center">
                <h5>{{ access_msg }}</h5>
            </div>
        </div>
    </span>
    <span v-if="can_access">
        <h3 class="card-title mb-4 mt-4">
            <button class="btn-lg btn" @click="back"><i class="fa fa-arrow-left"></i></button> {{ title }}
        </h3>
        <div class="row" v-if="!is_ready">
            <div class="col-12 text-center pad-top mb-5">
                <h4>{{ t['common.loading'] }}...</h4>
            </div>
        </div>
        <form v-on:submit.prevent="do_nothing" v-if="is_ready">
            <div class="form-group" v-for="field of definition.fields">
                <span
                    v-if="field.type === 'display' && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data))"
                    v-html="typeof field.display === 'function' ? field.display(data) : field.display"
                ></span>
                <span v-if="field.type.startsWith('select')  && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data, field.options))">
                    <label :for="uuid+field.field">{{ field.name }}</label>
                    <select
                        :id="uuid+field.field" 
                        :class="['form-control', (field.type.endsWith('.multiple') ? 'multi' : 'normal')]" 
                        v-model="data[field.field]"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                        :multiple="!!field.type.endsWith('.multiple')"
                        ref="input"
                    >
                        <option
                            v-for="option of field.options"
                            :value="option.value"
                            :selected="data[field.field] && (data[field.field] === option.value || (Array.isArray(data[field.field]) && data[field.field].includes(option.value)))"
                        >{{ typeof option.display === 'function' ? option.display(option) : option.display }}</option>
                    </select>
                    <small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
                </span>
                <span v-if="field.type === 'text' && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data))">
                    <label :for="uuid+field.field" style="display: inline">{{ field.name }} <span v-if="field.help" :title="field.help"><img src="/assets/info-circle-solid.svg" height="18"></span></label>
                    <input
                        type="text"
                        class="form-control"
                        :id="uuid+field.field"
                        v-model="data[field.field]"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :placeholder="field.placeholder"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                        ref="input"
                    >
                    <small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
                </span>
                <span v-if="field.type === 'textarea' && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data))">
                    <label :for="uuid+field.field">{{ field.name }}</label>
                    <textarea
                        class="form-control"
                        :id="uuid+field.field"
                        v-model="data[field.field]"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :placeholder="field.placeholder"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                        ref="input"
                    ></textarea>
                    <small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
                </span>
                <span v-if="field.type === 'json' && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data))">
                    <label :for="uuid+field.field">{{ field.name }}</label>
                    <textarea
                        class="form-control"
                        :id="uuid+field.field"
                        v-model="data[field.field]"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :placeholder="field.placeholder"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                        ref="input"
                        rows="10"
                    ></textarea>
                    <small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
                </span>
                <span v-if="field.type === 'password' && (Array.isArray(field.hidden) ? !field.hidden.includes(mode) : !field.hidden) && (typeof field.if !== 'function' || field.if(data))">
                    <label :for="uuid+field.field">{{ field.name }}</label>
                    <input
                        type="password"
                        class="form-control"
                        :id="uuid+field.field"
                        v-model="data[field.field]"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :placeholder="field.placeholder"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                        ref="input"
                    >
                    <input
                        type="password"
                        class="form-control"
                        :id="uuid+field.field+'-confirm'"
                        v-model="data[field.field+'-confirm']"
                        :required="Array.isArray(field.required) ? field.required.includes(mode) : field.required"
                        :placeholder="t['common.confirm'] + ' ' + field.name"
                        :readonly="mode === 'view' || (Array.isArray(field.readonly) ? field.readonly.includes(mode) : field.readonly)"
                    >
                    <small class="form-text" style="color: darkred;" v-if="field.error">{{ field.error }}</small>
                </span>
            </div>
        </form>
        <div class="col-12 text-right mb-4 mr-0 mt-2">
            <span style="color: darkred;" class="font-italic mr-3" v-if="error_message">{{ error_message }}</span>
            <span class="font-italic mr-3 text-muted" v-if="other_message">{{ other_message }}</span>
            <button
                class="btn btn-primary"
                type="button"
                v-if="mode !== 'view'"
                @click="save_click"
            >{{ t['common.save'] }}</button>                
        </div>
    </span>
</div>
`

const title_map = {
    insert: 'Create',
    update: 'Edit',
    view: 'View',
}

export default class FormComponent extends Component {
    static get selector() {
        return 'cobalt-form'
    }

    static get template() {
        return template
    }

    static get props() {
        return ['resource', 'form_id', 'initial_mode']
    }

    constructor() {
        super()

        this.definition = {}
        this.data = {}
        this.uuid = ''
        this.title = ''
        this.error_message = ''
        this.other_message = ''

        this.access_msg = ''
        this.can_access = false

        this.is_ready = false
        this.mode = ''
        this.id = ''
        this.t = {}
    }

    reset() {
        this.definition = {}
        this.data = {}
        this.uuid = ''
        this.title = ''
        this.error_message = ''
        this.other_message = ''
        this.is_ready = false
    }

    async vue_on_create(internal = false) {
        this.t = await T(
            'common.loading',
            'common.confirm',
            'common.save',
            'common.not_permission',
            'common.item_saved',
            'common.unknown_error',
            'common.field_required',
            'common.confirmation_not_match',
            'common.invalid_json'
        )

        if ( !internal ) {
            this.mode = this.initial_mode
            this.id = this.form_id
            this.resource_class = await resource_service.get(this.resource)

            if ( await this.resource_class.can(this.mode) ) {
                this.can_access = true
                this.access_msg = true
            } else {
                this.can_access = false
                this.access_msg = this.t['common.not_permission'].replace('ACTION', this.mode)
                return
            }

        } else {
            this.reset()
        }

        this.uuid = utility.uuid()
        await this.init()
        await this.load()
    }

    async init() {
        this.definition = this.resource_class.form_definition
        for ( const field of this.definition.fields ) {
            if ( field.type.startsWith('select.dynamic') ) {
                field._options = field._options || field.options
                const rsc = await resource_service.get(field._options.resource)
                const other_params = field._options.other_params || {}

                field.options = (await rsc.list(other_params)).map(item => {
                    return {
                        display: typeof field._options.display === 'function' ? field._options.display(item) : item[field._options.display || 'display'],
                        value: typeof field._options.value === 'function' ? field._options.value(item) : item[field._options.value || 'display'],
                    }
                })
            }
        }
    }

    async load() {
        if (this.mode !== 'insert') {
            this.data = await this.resource_class.get(this.id)
        }

        for ( const field of this.definition.fields ) {
            if ( field.type.endsWith('.multiple') && !this.data[field.field] ) {
                this.data[field.field] = []
            }

            if ( field.type === 'json' ) {
                this.data[field.field] = JSON.stringify(this.data[field.field], undefined, 4)
            }
        }

        this.title = title_map[this.mode] + ' ' + this.resource_class.item

        this.is_ready = true
        this.$nextTick(() => {
            if ( this.mode !== 'view' ) this.$refs.input[0].focus()
        })
    }

    async on_create() {
        this.id = this.data.id
        this.mode = 'update'
        if ( this.definition.handlers && this.definition.handlers.insert ) {
            await action_service.perform(this.definition.handlers.insert)
        }
        await this.vue_on_create(true)
        location_service.set_query(`mode=update&id=${this.id}`)
    }

    async on_update() {
        if ( this.definition.handlers && this.definition.handlers.update ) {
            await action_service.perform(this.definition.handlers.update)
        }
    }

    data_to_save() {
        const data = Object.assign({}, this.data)
        for ( const field of this.definition.fields ) {
            if ( field.type === 'json' ) {
                data[field.field] = JSON.parse(data[field.field])
            }
        }
        return data
    }

    data_to_restore(data) {
        const new_data = Object.assign({}, data)
        for ( const field of this.definition.fields ) {
            if ( field.type === 'json' ) {
                new_data[field.field] = JSON.stringify(data[field.field], undefined, 4)
            }
        }
        this.data = new_data
    }

    async save_click() {
        if ( !this.validate() ) return
        try {
            if (this.mode === 'insert') {
                this.data_to_restore(await this.resource_class.create(this.data_to_save()))
                await this.on_create()
            } else if (this.mode === 'update') {
                await this.resource_class.update(this.id, this.data_to_save())
                await this.on_update()
            }

            this.error_message = ''
            this.other_message = this.t['common.item_saved'].replace('ITEM', this.resource_class.item)
        } catch (e) {
            console.error(e)
            if ( e.response && e.response.data && e.response.data.message )
                this.error_message = e.response.data.message
            else this.error_message = this.t['common.unknown_error']
        }
    }

    is_json(string) {
        try {
            JSON.parse(string)
            return true
        } catch (e) {
            return false
        }
    }

    validate() {
        let valid = true
        for ( const field of this.definition.fields ) {
            if ( (Array.isArray(field.required) ? field.required.includes(this.mode) : field.required) && (!(field.field in this.data) || !this.data[field.field]) ) {
                field.error = this.t['common.field_required']
                valid = false
            } else if ( field.type === 'password' && this.data[field.field] !== this.data[field.field + '-confirm'] ) {
                field.error = field.name + ' ' + this.t['common.confirmation_not_match']
                valid = false
            } else if ( field.type === 'json' && !this.is_json(this.data[field.field]) ) {
                field.error = field.name + ' ' + this.t['common.invalid_json']
                valid = false
            } else {
                field.error = ''
            }
        }

        this.$forceUpdate()

        return valid
    }

    back() {
        return action_service.perform({
            text: '',
            type: 'resource',
            resource: this.resource,
            action: 'list',
        })
    }

    do_nothing() {}
}

