import { Component, h, Host, Method, Watch, Event, EventEmitter, State, Prop } from '@stencil/core'
import { HistoryItem, HistoryItemSelection } from '../gd-user-dashboard-list-item/user-dashboard-list-item'
import { format, isToday, isYesterday, isSameDay } from 'date-fns'
import { TagOptions } from '../gd-user-dashboard-bulk-tag-dialog/gd-user-dashboard-bulk-tag-dialog'

export type DashboardListType = 'favorites' | 'history' | 'shared'
export type HistoryItemTag = {
    count: number
    tag: string
}
export type EditableHistoryItem = {
    current: HistoryItem
    changes: HistoryItem
}

export type Datalist = {
    description: string
    url: string
    historyItems: Array<HistoryItem>
}

@Component({
    tag: 'gd-user-dashboard',
    styleUrl: 'gd-user-dashboard.scss',
    shadow: true,
})
export class GdUserDashboard {
    /**
     * dashboard list items
     */
    @Prop() items: Array<HistoryItem> = null

    /**
     * a list of tags to show in the filter by dropdown
     */
    @Prop() tags: Array<HistoryItemTag> = []

    /**
     * whether to split the history items by showing date headers
     */
    @Prop() showDateHeaders: boolean = true

    /**
     * an active datalist
     */
    @Prop() activeDatalist: Datalist

    /**
     * if the share as url functionality should be available or not
     */
    @Prop() canShareAsUrl: boolean = true

    /**
     * the currently visible list
     */
    @State() activeList: DashboardListType = 'history'

    /**
     * the tag the user has selected to filter by
     */
    @State() selectedTag: string

    /**
     * when editing a history item, its unsaved state will be stored here
     */
    @State() editableHistoryItem: EditableHistoryItem

    /**
     * holds the current list of selected history items
     */
    @State() selectedHistoryItems: Array<string> = []

    /**
     * controls when the export dialog shows
     */
    @State() showExportDialog: boolean = false

    /**
     * controls when the import dialog shows
     */
    @State() showImportDialog: boolean = false

    /**
     * controls when the bulk tag dialog shows
     */
    @State() showBulkTagDialog: boolean = false

    /**
     * controls when the datalist import dialog shows
     */
    @State() showDatalistImportDialog: boolean = false

    /**
     * an event that is fired when the list changes, would be a good time to refresh the list
     */
    @Event() activeListChanged: EventEmitter<string>

    /**
     * an event that is fired when the user selects a tag to filter by
     */
    @Event() filterChanged: EventEmitter<string>

    /**
     * an event that is triggered to inform the application that a history item needs to be updated
     */
    @Event() saveHistoryItem: EventEmitter<HistoryItem>

    /**
     * an event that is triggered to inform the application that one or more history items need to be deleted
     */
    @Event() deleteHistoryItems: EventEmitter<string[]>

    /**
     * an event triggered when the user clicks cancel on the datalist import dialog
     */
    @Event() cancelDatalistImport: EventEmitter<void>

    /**
     * used by the application to save changes that have been made to multiple history items
     */
    @Event() updateHistoryItems: EventEmitter<HistoryItem[]>

    /**
     * an exposed method to change the visible list
     * @param listType
     */
    @Method()
    async setActiveList(listType: DashboardListType) {
        if (listType == this.activeList) {
            return
        }

        this.activeList = listType
    }

    /**
     * on load, need to notify the application to load the requested list
     */
    componentDidLoad() {
        this.notifyOfListChange()
        this.handleActiveDatalistChanged()
    }

    /**
     * anytime the active list changes, trigger an event to let the user know
     */
    @Watch('activeList')
    notifyOfListChange() {
        this.activeListChanged.emit(this.activeList)
    }

    @Watch('activeDatalist')
    handleActiveDatalistChanged() {
        this.showDatalistImportDialog = this.activeDatalist ? true : false
    }

    @Watch('items')
    handleItemsChanged() {
        // reset selected history items when the list changes
        this.selectedHistoryItems = []
    }

    appendLeadingZeroes(n) {
        if (n <= 9) {
            return '0' + n
        }
        return n
    }

    filterByTag(tag: string) {
        this.selectedTag = tag
        this.filterChanged.emit(tag)
    }

    editHistoryItem(historyItem: HistoryItem) {
        this.editableHistoryItem = {
            current: historyItem,
            changes: {
                ...historyItem,
            },
        }
    }

    stopEditingHistoryItem() {
        this.editableHistoryItem = undefined
    }

    saveHistoryItemChanges(historyItem: HistoryItem) {
        this.saveHistoryItem.emit(historyItem)
        this.stopEditingHistoryItem()
    }

    selectedItemChanged(item: HistoryItemSelection) {
        if (!item.selected) {
            // remove from list of selected items
            this.selectedHistoryItems = [].concat(this.selectedHistoryItems.filter(id => id != item.historyItem.id))
        } else if (item.selected && !this.selectedHistoryItems.find(id => id == item.historyItem.id)) {
            // add to list of selected history items
            this.selectedHistoryItems = [].concat(this.selectedHistoryItems, item.historyItem.id)
        }
    }

    openExportDialog() {
        if (this.selectedHistoryItems.length <= 0) {
            return
        }

        this.showExportDialog = true
    }

    openBulkTagDialog() {
        if (this.selectedHistoryItems.length <= 0) {
            return
        }

        this.showBulkTagDialog = true
    }

    selectAllHistoryItems() {
        this.selectedHistoryItems = [].concat(this.items.map((item: HistoryItem) => item.id))
    }

    deselectAllHistoryItems() {
        this.selectedHistoryItems = []
    }

    confirmAndDeleteHistoryItems(historyItemIds: string[]) {
        if (!historyItemIds.length || !confirm(`Are you sure you want to delete these ${historyItemIds.length} items?`)) {
            return
        }

        this.deleteHistoryItems.emit(historyItemIds)
    }

    bulkAddTags(tagOptions: TagOptions) {
        // compile a list of the modified history items and add the requested tags
        let historyItems = this.items
            .filter(item => tagOptions.historyItemIds.includes(item.id))
            .map(historyItem => {
                if (!historyItem.tags) historyItem.tags = []

                // add tags to history item and remove duplicates (if it already has the entered tag)
                historyItem.tags = [...new Set(historyItem.tags.concat(tagOptions.tags))]

                return historyItem
            })

        this.updateHistoryItems.emit(historyItems)
    }

    /**
     * renders the user dashboard
     */
    render() {
        return (
            <Host>
                <h2 class="py-5">My Dashboard</h2>

                <div style={{ position: 'relative' }}>
                    <bs-nav variant="tabs" class="nav">
                        <bs-nav-item href="javascript:;" onClick={() => this.setActiveList('favorites')} active={this.activeList == 'favorites'}>
                            <bs-icon icon="fas fa-heart" class="mr-2"></bs-icon>
                            My Favorites
                        </bs-nav-item>

                        <bs-nav-item href="javascript:;" onClick={() => this.setActiveList('history')} active={this.activeList == 'history'}>
                            <bs-icon icon="fab fa-history" class="mr-2"></bs-icon>
                            Site History
                        </bs-nav-item>

                        <bs-nav-item href="javascript:;" onClick={() => this.setActiveList('shared')} active={this.activeList == 'shared'}>
                            <bs-icon icon="fab fa-share" class="mr-2"></bs-icon>
                            Shared
                        </bs-nav-item>
                    </bs-nav>

                    <div style={{ position: 'absolute', top: '0', right: '0' }}>
                        <bs-form-row>
                            <bs-form-group stack={false}>
                                <bs-form-label style={{ fontWeight: '600' }}>Filter By</bs-form-label>
                                <bs-form-control as="select" name="filterByTag" onBsInput={(e: any) => this.filterByTag(e.detail?.target?.value)}>
                                    <option value="">Select</option>

                                    {this.tags.map(tag => (
                                        <option value={tag.tag}>
                                            {tag.tag} ({tag.count})
                                        </option>
                                    ))}
                                </bs-form-control>
                            </bs-form-group>
                        </bs-form-row>
                    </div>
                </div>

                <bs-button-group class="py-4 pl-1" role="toolbar" aria-label="Bulk actions">
                    <bs-button-group class="mr-3" aria-label="Bulk select/deselect">
                        {this.selectedHistoryItems.length > 0 ? (
                            <bs-button class="deselect" variant="outline-secondary" title="Deselect all" onClick={this.deselectAllHistoryItems.bind(this)}>
                                <bs-icon icon="fas fa-square" size={20} />
                                &nbsp;
                            </bs-button>
                        ) : (
                            <bs-button variant="outline-secondary" title="Select all" onClick={this.selectAllHistoryItems.bind(this)}>
                                <bs-icon icon="fas fa-square" size={20} />
                                &nbsp;
                            </bs-button>
                        )}
                    </bs-button-group>

                    <bs-button-group class="mr-3" aria-label="Bulk history actions">
                        <bs-button variant="outline-secondary" onClick={() => (this.showImportDialog = true)}>
                            <bs-icon icon="fab fa-file-import" size={20} class="mr-2" />
                            Import
                        </bs-button>

                        <bs-button variant="outline-secondary" disabled={this.selectedHistoryItems.length <= 0} onClick={() => this.openExportDialog()}>
                            <bs-icon icon="fab fa-file-export" size={20} class="mr-2" />
                            Export
                        </bs-button>
                    </bs-button-group>

                    <bs-button-group class="mr-3" aria-label="Bulk history actions">
                        <bs-button variant="outline-secondary" disabled={this.selectedHistoryItems.length <= 0} onClick={() => this.openBulkTagDialog()}>
                            <bs-icon icon="fab fa-tags" size={20} class="mr-2" />
                            Tag
                        </bs-button>

                        <bs-button
                            variant="outline-secondary"
                            disabled={this.selectedHistoryItems.length <= 0}
                            onClick={() => this.confirmAndDeleteHistoryItems(this.selectedHistoryItems)}
                        >
                            <bs-icon icon="fab fa-trash-alt" size={20} class="mr-2" />
                            Delete
                        </bs-button>
                    </bs-button-group>
                </bs-button-group>

                <div class="list-items">
                    <bs-list-group horizontal="sm" flush={true}>
                        {this.items == null && (
                            <div class="loading">
                                <bs-spinner variant="primary" size="sm" />
                                <div>Loading {this.activeList}...</div>
                            </div>
                        )}

                        {this.items?.length == 0 && <div>You don't have any {this.activeList} items yet.</div>}

                        {this.items?.map((item, index, items) => {
                            const renderedItem = (
                                <gd-user-dashboard-list-item
                                    key={item.id}
                                    item={item}
                                    allow-edit={this.activeList == 'favorites' || this.activeList == 'shared'}
                                    show-shared-by={this.activeList == 'shared'}
                                    onEditItem={e => this.editHistoryItem(e.detail)}
                                    selected={this.selectedHistoryItems.findIndex(id => id == item.id) > -1}
                                    onItemSelected={(e: CustomEvent<HistoryItemSelection>) => this.selectedItemChanged(e.detail)}
                                    alternateRow={(index + 2) % 2 ? 'even' : 'odd'}
                                />
                            )

                            if (this.showDateHeaders) {
                                const createdDate = item.created ? new Date(item.created) : null
                                const prevCreatedDate = index > 0 && items[index - 1].created ? new Date(items[index - 1].created) : null

                                // should show date header if the date changed or if this is the first item
                                if (index == 0 || !isSameDay(createdDate, prevCreatedDate)) {
                                    return (
                                        <div>
                                            {createdDate && (
                                                <h5 class={`${index > 0 && 'mt-4'} mb-3`}>
                                                    {isToday(createdDate) && 'Today, '}
                                                    {isYesterday(createdDate) && 'Yesterday, '}
                                                    {format(createdDate, 'EEEE, MMMM d, yyyy')}
                                                </h5>
                                            )}

                                            {renderedItem}
                                        </div>
                                    )
                                }
                            }

                            return renderedItem
                        })}
                    </bs-list-group>
                </div>

                {this.renderEditFavoriteDialog()}

                <gd-user-dashboard-export-dialog
                    show={this.showExportDialog}
                    historyItemIds={this.selectedHistoryItems}
                    onClosed={() => (this.showExportDialog = false)}
                    canShareAsUrl={this.canShareAsUrl}
                />
                <gd-user-dashboard-import-dialog show={this.showImportDialog} onClosed={() => (this.showImportDialog = false)} />
                <gd-user-dashboard-import-datalist-dialog show={this.showDatalistImportDialog} onClosed={() => this.cancelDatalistImport.emit()} datalist={this.activeDatalist} />
                <gd-user-dashboard-bulk-tag-dialog
                    show={this.showBulkTagDialog}
                    historyItemIds={this.selectedHistoryItems}
                    onClosed={() => (this.showBulkTagDialog = false)}
                    onAddTags={(e: any) => this.bulkAddTags(e.detail)}
                />
            </Host>
        )
    }

    private renderEditFavoriteDialog() {
        return (
            <bs-modal static={true} show={this.editableHistoryItem !== undefined} onClose={() => this.stopEditingHistoryItem()}>
                <bs-modal-header modal-title='<bs-icon size="20" class="mr-2" icon="fab fa-edit"></bs-icon> Edit Favorite' />

                <bs-modal-body>
                    <bs-form-row>
                        <bs-form-group stack={false}>
                            <bs-form-label>Label</bs-form-label>
                            <bs-form-control
                                control-id="label"
                                as="input"
                                name="label"
                                type="text"
                                value={this.editableHistoryItem?.current.label}
                                onBsInput={(e: any) => (this.editableHistoryItem.changes.label = e.detail.target.value)}
                            />
                        </bs-form-group>
                    </bs-form-row>

                    <bs-form-row>
                        <bs-form-group stack={false}>
                            <bs-form-label>Tags</bs-form-label>
                            <gd-tag-input
                                defaultValue={this.editableHistoryItem?.current.tags}
                                variant="pills"
                                placeholder="Add tag..."
                                onTagsChange={(event: any) => {
                                    if (!this.editableHistoryItem?.changes) return
                                    this.editableHistoryItem.changes.tags = event.detail
                                }}
                            />
                        </bs-form-group>
                    </bs-form-row>
                </bs-modal-body>

                <bs-modal-footer>
                    <bs-button variant="secondary" onClick={() => this.stopEditingHistoryItem()}>
                        Close
                    </bs-button>

                    <bs-button variant="primary" onClick={() => this.saveHistoryItemChanges(this.editableHistoryItem.changes)}>
                        Save changes
                    </bs-button>
                </bs-modal-footer>
            </bs-modal>
        )
    }
}
