<template>
  <div class="model-crud" :class="getModelSlug()">
    <nav class="breadcrumb" aria-label="breadcrumbs">
      <ul>
        <li>
          <router-link :to="getModelPathPrefix() + '/'">{{ getModelIndexTitle() }}</router-link>
        </li>
        <li v-if="isRow">
          <router-link :to="getModelPathPrefix() + '/' + record._parent_uuid">{{ renderParentHeaderLabel(record) }}</router-link>
          <!-- &nbsp; {{ renderParentHeaderLabel(record) }} -->
        </li>
        <li v-if="mode == 'creating'">
          &nbsp; New
        </li>
        <li v-if="mode == 'viewing'">
          &nbsp; {{ renderHeaderLabel(record) }}
        </li>
        <li v-if="mode == 'editing' || mode == 'deleting'">
          <router-link :to="getRowPathPrefix() + '/' + record.uuid">{{ renderHeaderLabel(record) }}</router-link>
        </li>
        <li v-if="mode == 'editing'">
          &nbsp; Edit
        </li>
        <li v-if="mode == 'deleting'">
          &nbsp; Delete
        </li>
      </ul>
    </nav>

    <slot></slot>

    <div v-if="showButtons"
         class="buttons">
      <b-button type="is-primary"
                icon-left="save"
                :disabled="saveDisabled"
                @click="save()">
        Save Data
      </b-button>
      <b-button v-if="mode == 'creating'"
                tag="router-link"
                :to="getModelPathPrefix() + '/'">
        Cancel
      </b-button>
      <b-button v-if="mode == 'editing'"
                tag="router-link"
                :to="getViewURL()">
        Cancel
      </b-button>
    </div>

    <div v-if="shouldAllowEdit() || shouldAllowDelete()" class="buttons">

      <br /><br />

      <b-button v-if="shouldAllowEdit()"
                type="is-primary"
                icon-left="edit"
                tag="router-link"
                :to="getEditURL()">
        Edit This
      </b-button>

      <b-button v-if="shouldAllowDelete()"
                type="is-danger"
                icon-left="trash"
                tag="router-link"
                :to="getDeleteURL()">
        Delete This
      </b-button>
    </div>

    <slot name="quick-entry"></slot>

    <div v-if="hasRows && mode == 'viewing'">
      <slot name="row-filters"></slot>
      <b-menu>
        <b-menu-list>
          <b-menu-item v-for="row in rowData.data"
                       :key="row.uuid"
                       tag="router-link"
                       :to="getRowRoute(row)">
            <template slot="label" slot-scope="props">
              <span v-html="renderRowLabel(row)"></span>
            </template>
          </b-menu-item>
        </b-menu-list>
      </b-menu>
      <b-pagination v-if="rowsPaginated"
                    :total="rowData.total"
                    :current.sync="rowPage"
                    :per-page="rowsPerPage"
                    @change="changeRowPagination"
                    >
        <!-- icon-pack="fas" -->
      </b-pagination>
    </div>

    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
    name: 'ByjoveModelCrud',
    props: {
        mode: String,
        modelName: String,
        modelSlug: String,
        modelTitle: String,
        modelTitlePlural: String,
        modelIndexTitle: String,
        modelPermissionPrefix: String,
        modelPathPrefix: String,
        modelRoutePrefix: String,
        apiIndexUrl: String,
        apiObjectUrl: String,
        labelRenderer: Function,
        parentHeaderLabelRenderer: Function,
        headerLabelRenderer: Function,
        rowLabelRenderer: Function,
        hasRows: {
            type: Boolean,
            default: false,
        },
        rowsPaginated: {
            type: Boolean,
            default: true,
        },
        rowsPerPage: {
            type: Number,
            default: 20,
        },
        rowRouteGetter: Function,
        apiRowsUrl: String,
        isRow: {
            type: Boolean,
            default: false,
        },
        rowPathPrefix: String,
        rowFilters: {
            type: Function,
            default: (uuid) => {
                return JSON.stringify([{field: 'batch_uuid', op: 'eq', value: uuid}])
            },
        },
        allowEdit: {
            type: Boolean,
            default: true,
        },
        allowDelete: {
            type: Boolean,
            default: false,
        },
        hideButtons: {
            type: Boolean,
            default: false,
        },
        saveDisabled: {
            type: Boolean,
            default: false,
        },
    },
    data: function() {
        return {
            record: {},
            rowData: {},
            rowPage: 1,
        }
    },

    computed: {
        showButtons: function() {
            if (this.hideButtons) {
                return false
            }
            if (this.mode == 'creating') {
                return true
            }
            if (this.mode == 'editing') {
                return true
            }
            return false
        },
    },

    // TODO: why doesn't beforeRouteUpdate() work instead?
    // cf. https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
    watch: {
        '$route' (to, from) {
            if (to.name == (this.getModelRoutePrefix() + '.edit') && !this.hasModelPerm('edit')) {
                this.$buefy.toast.open({
                    message: "You do not have permission to access that page.",
                    type: 'is-danger',
                    position: 'is-bottom',
                })
                this.$router.push(this.getModelPathPrefix() + '/')
            }

            // re-fetch record in case it was just changed
            if (to.name != (this.getModelRoutePrefix() + '.new')) {
                this.fetch(to.params.uuid)
            }
        },
    },

    // beforeRouteUpdate (to, from, next) {
    //     // re-fetch record in case it was just changed
    //     if (to.name != (this.getModelRoutePrefix() + '.new')) {
    //         this.fetch(to.params.uuid)
    //     }
    //     next()
    // },

    mounted() {

        // TODO: this seems like a "good" idea, but in practice, when reloading
        // the page/app via browser (Ctrl+Shift+R), the app must re-fetch the
        // session details before it knows which user/permissions are in
        // effect, and that takes "too long" which means these checks fail!

        // // redirect if user doesn't have permission to be here
        // if ((this.mode == 'viewing' && !this.hasModelPerm('view'))
        //     || (this.mode == 'creating' && !this.hasModelPerm('create'))
        //     || (this.mode == 'editing' && !this.hasModelPerm('edit'))
        //     || (this.mode == 'deleting' && !this.hasModelPerm('delete'))) {
        //     this.$buefy.toast.open({
        //         message: "You do not have permission to access that page.",
        //         type: 'is-danger',
        //         position: 'is-bottom',
        //     })
        //     this.$router.push(this.getModelPathPrefix() + '/')
        //     return
        // }

        // fetch initial page data unless 'creating'
        if (this.mode != 'creating') {
            this.fetch(this.$route.params.uuid)
        }
    },

    methods: {

        getModelSlug() {
            if (this.modelSlug) {
                return this.modelSlug
            }
            return this.modelName.toLowerCase() + 's'
        },

        getModelTitle() {
            if (this.modelTitle) {
                return this.modelTitle
            }
            return this.modelName
        },

        getModelTitlePlural() {
            if (this.modelTitlePlural) {
                return this.modelTitlePlural
            }
            return this.getModelTitle() + 's'
        },

        getModelIndexTitle() {
            if (this.modelIndexTitle) {
                return this.modelIndexTitle
            }
            return this.getModelTitlePlural()
        },

        getModelPathPrefix() {
            if (this.modelPathPrefix) {
                return this.modelPathPrefix
            }
            return '/' + this.getModelSlug()
        },

        getModelRoutePrefix() {
            if (this.modelRoutePrefix) {
                return this.modelRoutePrefix
            }
            return this.getModelSlug()
        },

        getRowPathPrefix() {
            if (this.rowPathPrefix) {
                return this.rowPathPrefix
            }
            return '/' + this.getModelSlug() + '/rows'
        },

        getViewURL() {
            if (this.isRow) {
                return `${this.getRowPathPrefix()}/${this.record.uuid}`
            }
            return `${this.getModelPathPrefix()}/${this.record.uuid}`
        },

        getEditURL() {
            if (this.isRow) {
                return `${this.getRowPathPrefix()}/${this.record.uuid}/edit`
            }
            return `${this.getModelPathPrefix()}/${this.record.uuid}/edit`
        },

        getDeleteURL() {
            if (this.isRow) {
                return `${this.getRowPathPrefix()}/${this.record.uuid}/delete`
            }
            return `${this.getModelPathPrefix()}/${this.record.uuid}/delete`
        },

        getApiIndexUrl() {
            if (this.apiIndexUrl) {
                return this.apiIndexUrl
            }
            return '/api/' + this.getModelSlug()
        },

        getApiObjectUrl() {
            if (this.apiObjectUrl) {
                return this.apiObjectUrl
            }
            let url = this.getApiIndexUrl()
            // drop trailing 's' then add slash
            return url.slice(0, -1) + '/'
        },

        getApiRowsUrl() {
            return this.apiRowsUrl
        },

        getModelPermissionPrefix() {
            if (this.modelPermissionPrefix) {
                return this.modelPermissionPrefix
            }
            return this.getModelSlug()
        },

        hasPerm(perm) {

            // if user is root then assume permission
            if (this.$store.state.user && this.$store.state.user.is_root) {
                return true
            }

            // otherwise do true perm check for user
            return this.$store.state.permissions.includes(perm)
        },

        hasModelPerm(perm) {

            // do normal check, but first add prefix
            let prefix = this.getModelPermissionPrefix()
            return this.hasPerm(prefix + '.' + perm)
        },

        renderLabel(obj) {
            if (this.labelRenderer) {
                return this.labelRenderer(obj)
            }
            return obj._str
        },

        renderHeaderLabel(obj) {
            if (this.headerLabelRenderer) {
                return this.headerLabelRenderer(obj)
            }
            return this.renderLabel(obj)
        },

        renderParentHeaderLabel(obj) {
            if (this.parentHeaderLabelRenderer) {
                return this.parentHeaderLabelRenderer(obj)
            }
            return this.renderLabel(obj)
        },

        renderRowLabel(row) {
            if (this.rowLabelRenderer) {
                return this.rowLabelRenderer(row)
            }
            return row._str
        },

        fetch(uuid) {
            this.$http.get(this.getApiObjectUrl() + uuid).then(response => {
                this.record = response.data.data
                this.$emit('refresh', this.record)
                if (this.hasRows) {
                    this.fetchRows(uuid)
                }
            }, response => {
                if (response.status == 403) { // forbidden; redirect to model index
                    this.$buefy.toast.open({
                        message: "You do not have permission to access that page.",
                        type: 'is-danger',
                        position: 'is-bottom',
                    })
                    this.$router.push(this.getModelPathPrefix() + '/')
                } else {
                    this.$buefy.toast.open({
                        message: "Failed to fetch page data!",
                        type: 'is-danger',
                        position: 'is-bottom',
                    })
                }
            })

        },

        fetchRows(uuid) {
            let params = {
                filters: this.rowFilters(uuid),
                orderBy: 'modified',
                ascending: 0,
            }
            if (this.rowsPaginated) {
                params.per_page = this.rowsPerPage
                params.page = this.rowPage
            }
            this.$http.get(this.getApiRowsUrl(), {params: params}).then(response => {
                this.rowData = response.data

            }, response => {
                if (response.status == 403) { // forbidden; redirect to home page
                    this.$buefy.toast.open({
                        message: "You do not have permission to access that page.",
                        type: 'is-danger',
                        position: 'is-bottom',
                    })
                    this.$router.push('/')
                } else {
                    this.$buefy.toast.open({
                        message: "Failed to fetch page data!",
                        type: 'is-danger',
                        position: 'is-bottom',
                    })
                }
            })
        },

        getRowRoute(row) {
            if (this.rowRouteGetter) {
                return this.rowRouteGetter(row)
            }
            return this.getRowPathPrefix() + '/' + row.uuid
        },

        changeRowPagination(value) {
            this.fetchRows(this.record.uuid)
        },

        shouldAllowEdit() {
            if (!this.allowEdit) {
                return false
            }
            if (this.mode != 'viewing') {
                return false
            }
            if (!this.hasModelPerm('edit')) {
                return false
            }
            return true
        },

        shouldAllowDelete() {
            if (!this.allowDelete) {
                return false
            }
            if (this.mode == 'creating' || this.mode == 'deleting') {
                return false
            }
            if (!this.hasModelPerm('delete')) {
                return false
            }
            return true
        },

        save() {
            let url = this.getApiIndexUrl()
            if (this.mode != 'creating') {
                url = this.getApiObjectUrl() + this.record.uuid
            }
            this.$emit('save', url)
        },
    },
}
</script>
