import {Component} from '../../vues6.js'
import {Resource, ResourceActions} from '../Resource.js'
import {uuid} from '../util.js'
import {ActionService} from '../service/Action.service.js'

export const Status = Object.freeze({
    loading: 'loading',
    ready: 'ready',
    errorUnsupported: 'errorUnsupported',
    saving: 'saving',
})

const template = `
<div class="card shadow mb-4">
    <div class="card-header py-3">
        <h6 class="m-0 font-weight-bold text-primary">
            {{ title }}
        </h6>
    </div>
    <div class="card-body">
        <div v-if="status === 'loading'">Loading...</div>
        <div class="row m-5" v-if="status === 'errorUnsupported'">
            <div class="col-12 text-center">
                <h4 class="pad-top">This resource does not support listing.</h4>
            </div>
        </div>
        <div class="" v-if="status === 'ready'">
            <div class="actions" style="display: flex; flex-direction: row; justify-content: end">
                <span v-for="action of actions">
                    <cobalt-action-button v-if="action.overall" :action="action" @perform="() => onAction(action)"></cobalt-action-button>
                </span>
            </div>
            <table class="table table-bordered display compact" :id="'table-' + id" width="99%" cellspacing="0">
                <thead>
                    <tr>
                        <th>#</th>
                        <th v-for="col of columns">{{ col.display }}</th>
                        <th></th>
                    </tr>
                </thead>
                <tfoot>
                    <tr>
                        <th>#</th>
                        <th v-for="col of columns">{{ col.display }}</th>
                        <th></th>
                    </tr>
                </tfoot>
                <tbody></tbody>
            </table>
        </div>
    </div>
</div>
`
export class ListingComponent extends Component {
    static get selector() { return 'cobalt-listing' }
    static get template() { return template }
    static get props() { return ['resourcekey'] }

    constructor() {
        super()

        this.status = Status.loading
        this.title = 'Loading...'
        this.rows = []
        this.columns = []
        this.actions = []
        this.unregister = []
        this.id = uuid()

        /** @var {Resource} */
        this.resource = undefined
    }

    vue_on_destroy() {
        this.unregister.forEach(actionName => {
            if ( window[actionName] ) {
                delete window[actionName]
            }
        })
    }

    async vue_on_create() {
        this.resource = await Resource.get(this.resourcekey)
        this.title = this.resource.plural()

        if ( !this.resource.supports(ResourceActions.read) ) {
            this.status = Status.errorUnsupported
            return
        }

        await this.load()
    }

    async load(reload = false) {
        this.columns = [...this.resource.configuration.fields].filter(col => {
            return !(col.hideOn && col.hideOn.listing)
        })

        if ( !reload && this.resource.supports(ResourceActions.create) ) {
            this.actions.push({
                title: 'Add New',
                color: 'success',
                icon: 'fa-plus',
                overall: true,
                type: 'resource',
                resource: this.resource.key,
                action: 'insert',
                defer: true,
            })
        }

        if ( !reload && this.resource.supports(ResourceActions.update) ) {
            this.actions.push({
                title: 'Edit',
                color: 'primary',
                icon: 'fa-edit',
                type: 'resource',
                resource: this.resource.key,
                action: 'update',
                defer: true,
            })
        } else if ( !reload && this.resource.supports(ResourceActions.read) ) {
            this.actions.push({
                title: 'View',
                color: 'primary',
                icon: 'fa-eye',
                type: 'resource',
                resource: this.resource.key,
                action: 'view',
                defer: true,
            })
        }

        if ( !reload && this.resource.supports(ResourceActions.delete) ) {
            this.actions.push({
                title: 'Delete',
                color: 'danger',
                icon: 'fa-trash-alt',
                type: 'resource',
                resource: this.resource.key,
                action: 'delete',
                defer: true,
            })
        }

        this.rows = await this.resource.read()
        this.rows.forEach((row, idx) => row.idx = idx)

        const order = []
        this.columns.forEach((col, i) => {
            if ( col.sort ) {
                order.push([i+1, col.sort])
            }
        })

        this.status = Status.ready

        this.$nextTick(() => {
            $(`#table-${this.id}`).DataTable({
                order,
                data: this.rows,
                columns: [
                    {
                        data: 'idx',
                        render: data => `${data + 1}`,
                    },
                    ...this.columns.map((col, idx) => {
                        return {
                            data: col.key,
                            render: (data, type) => {
                                if ( type === 'display' ) {
                                    if ( col.renderer === 'html' ) {
                                        return String(data || '')
                                    } else if ( col.renderer === 'bool' ) {
                                        return data ? 'Yes' : 'No'
                                    } else if ( col.renderer === 'date' && data ) {
                                        return (new Date(data)).toDisplay()
                                    } else if ( col.renderer === 'time' && data ) {
                                        return (new Date(data)).toLocaleTimeString()
                                    } else if ( col.renderer === 'datetime' && data ) {
                                        return `${(new Date(data)).toDisplay()} ${(new Date(data)).toLocaleTimeString()}`
                                    }

                                    return String(data || '-').replace(/</g, '&lt;').replace(/>/, '&gt;')
                                }

                                return data
                            }
                        }
                    }),
                    {
                        data: 'idx',
                        render: (data, type, row) => {
                            if ( type === 'display' ) {
                                const html = []

                                this.actions.forEach((action, idx) => {
                                    if ( action.overall ) return

                                    const actionName = `action_${uuid().replace(/-/g, '')}`
                                    this.unregister.push(actionName)
                                    window[actionName] = () => {
                                        this.onAction(action, row)
                                    }

                                    html.push(`
                                        <a
                                            class="btn btn-sm btn-${action.color}"
                                            href="#"
                                            title="${action.title}"
                                            onclick="${actionName}()"
                                        >
                                            <i class="fa ${action.icon}"></i>
                                        </a>
                                    `)
                                })

                                return html.join('')
                            }

                            return data
                        }
                    },
                ],
            })
        })
    }

    async onAction(action, row) {
        await ActionService.get().perform(action, row, async () => {
            this.status = Status.loading
            await this.load(true)
        })
    }
}
