// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {FormattedMessage} from 'react-intl'

import {Constants} from '../constants'
import {BlockIcons} from '../blockIcons'
import {IPropertyTemplate} from '../blocks/board'
import {Card, MutableCard} from '../blocks/card'
import {BoardTree} from '../viewModel/boardTree'
import mutator from '../mutator'
import {Utils} from '../utils'

import MenuWrapper from '../widgets/menuWrapper'

import {CardDialog} from './cardDialog'
import RootPortal from './rootPortal'
import {TableRow} from './tableRow'
import ViewHeader from './viewHeader'
import ViewTitle from './viewTitle'
import TableHeaderMenu from './tableHeaderMenu'

import './tableComponent.scss'
import {HorizontalGrip} from './horizontalGrip'

import {MutableBoardView} from '../blocks/boardView'

type Props = {
    boardTree?: BoardTree
    showView: (id: string) => void
    setSearchText: (text: string) => void
}

type State = {
    shownCard?: Card
}

class TableComponent extends React.Component<Props, State> {
    private draggedHeaderTemplate: IPropertyTemplate
    private cardIdToRowMap = new Map<string, React.RefObject<TableRow>>()
    private cardIdToFocusOnRender: string
    state: State = {}

    shouldComponentUpdate(): boolean {
        return true
    }

    render(): JSX.Element {
        const {boardTree, showView} = this.props

        if (!boardTree || !boardTree.board) {
            return (
                <div>
                    <FormattedMessage
                        id='TableComponent.loading'
                        defaultMessage='Loading...'
                    />
                </div>
            )
        }

        const {board, cards, activeView} = boardTree
        const titleRef = React.createRef<HTMLDivElement>()

        this.cardIdToRowMap.clear()

        return (
            <div className='TableComponent octo-app'>
                {this.state.shownCard &&
                <RootPortal>
                    <CardDialog
                        boardTree={boardTree}
                        card={this.state.shownCard}
                        onClose={() => this.setState({shownCard: undefined})}
                    />
                </RootPortal>}
                <div className='octo-frame'>
                    <ViewTitle
                        key={board.id + board.title}
                        board={board}
                    />

                    <div className='octo-table'>
                        <ViewHeader
                            boardTree={boardTree}
                            showView={showView}
                            setSearchText={this.props.setSearchText}
                            addCard={this.addCard}
                        />

                        {/* Main content */}

                        <div className='octo-table-body'>

                            {/* Headers */}

                            <div
                                className='octo-table-header'
                                id='mainBoardHeader'
                            >
                                <div
                                    id='mainBoardHeader'
                                    ref={titleRef}
                                    className='octo-table-cell title-cell header-cell'
                                    style={{overflow: 'unset', width: this.columnWidth(Constants.titleColumnId)}}
                                >
                                    <MenuWrapper>
                                        <div
                                            className='octo-label'
                                            style={{cursor: 'pointer'}}
                                        >
                                            <FormattedMessage
                                                id='TableComponent.name'
                                                defaultMessage='Name'
                                            />
                                        </div>
                                        <TableHeaderMenu
                                            boardTree={boardTree}
                                            templateId='__name'
                                        />
                                    </MenuWrapper>

                                    <div className='octo-spacer'/>

                                    <HorizontalGrip
                                        onDrag={(offset) => {
                                            const originalWidth = this.columnWidth(Constants.titleColumnId)
                                            const newWidth = Math.max(Constants.minColumnWidth, originalWidth + offset)
                                            titleRef.current.style.width = `${newWidth}px`
                                        }}
                                        onDragEnd={(offset) => {
                                            Utils.log(`onDragEnd offset: ${offset}`)
                                            const originalWidth = this.columnWidth(Constants.titleColumnId)
                                            const newWidth = Math.max(Constants.minColumnWidth, originalWidth + offset)
                                            titleRef.current.style.width = `${newWidth}px`

                                            const columnWidths = {...activeView.columnWidths}
                                            if (newWidth !== columnWidths[Constants.titleColumnId]) {
                                                columnWidths[Constants.titleColumnId] = newWidth

                                                const newView = new MutableBoardView(activeView)
                                                newView.columnWidths = columnWidths
                                                mutator.updateBlock(newView, activeView, 'resize column')
                                            }
                                        }}
                                    />
                                </div>

                                {/* Table header row */}

                                {board.cardProperties.
                                    filter((template) => activeView.visiblePropertyIds.includes(template.id)).
                                    map((template) => {
                                        const headerRef = React.createRef<HTMLDivElement>()
                                        return (<div
                                            key={template.id}
                                            ref={headerRef}
                                            style={{overflow: 'unset', width: this.columnWidth(template.id)}}
                                            className='octo-table-cell header-cell'

                                            onDragOver={(e) => {
                                                e.preventDefault(); (e.target as HTMLElement).classList.add('dragover')
                                            }}
                                            onDragEnter={(e) => {
                                                e.preventDefault(); (e.target as HTMLElement).classList.add('dragover')
                                            }}
                                            onDragLeave={(e) => {
                                                e.preventDefault(); (e.target as HTMLElement).classList.remove('dragover')
                                            }}
                                            onDrop={(e) => {
                                                e.preventDefault(); (e.target as HTMLElement).classList.remove('dragover'); this.onDropToColumn(template)
                                            }}
                                        >
                                            <MenuWrapper>
                                                <div
                                                    className='octo-label'
                                                    style={{cursor: 'pointer'}}
                                                    draggable={true}
                                                    onDragStart={() => {
                                                        this.draggedHeaderTemplate = template
                                                    }}
                                                    onDragEnd={() => {
                                                        this.draggedHeaderTemplate = undefined
                                                    }}
                                                >{template.name}</div>
                                                <TableHeaderMenu
                                                    boardTree={boardTree}
                                                    templateId={template.id}
                                                />
                                            </MenuWrapper>

                                            <div className='octo-spacer'/>

                                            <HorizontalGrip
                                                onDrag={(offset) => {
                                                    const originalWidth = this.columnWidth(template.id)
                                                    const newWidth = Math.max(Constants.minColumnWidth, originalWidth + offset)
                                                    headerRef.current.style.width = `${newWidth}px`
                                                }}
                                                onDragEnd={(offset) => {
                                                    Utils.log(`onDragEnd offset: ${offset}`)
                                                    const originalWidth = this.columnWidth(template.id)
                                                    const newWidth = Math.max(Constants.minColumnWidth, originalWidth + offset)
                                                    headerRef.current.style.width = `${newWidth}px`

                                                    const columnWidths = {...activeView.columnWidths}
                                                    if (newWidth !== columnWidths[template.id]) {
                                                        columnWidths[template.id] = newWidth

                                                        const newView = new MutableBoardView(activeView)
                                                        newView.columnWidths = columnWidths
                                                        mutator.updateBlock(newView, activeView, 'resize column')
                                                    }
                                                }}
                                            />
                                        </div>)
                                    })}
                            </div>

                            {/* Rows, one per card */}

                            {cards.map((card) => {
                                const openButonRef = React.createRef<HTMLDivElement>()
                                const tableRowRef = React.createRef<TableRow>()

                                let focusOnMount = false
                                if (this.cardIdToFocusOnRender && this.cardIdToFocusOnRender === card.id) {
                                    this.cardIdToFocusOnRender = undefined
                                    focusOnMount = true
                                }

                                const tableRow = (<TableRow
                                    key={card.id}
                                    ref={tableRowRef}
                                    boardTree={boardTree}
                                    card={card}
                                    focusOnMount={focusOnMount}
                                    onSaveWithEnter={() => {
                                        console.log('WORKING')
                                        if (cards.length > 0 && cards[cards.length - 1] === card) {
                                            this.addCard(false)
                                        }
                                        console.log('STILL WORKING')
                                    }}
                                />)

                                this.cardIdToRowMap.set(card.id, tableRowRef)

                                return tableRow
                            })}

                            {/* Add New row */}

                            <div className='octo-table-footer'>
                                <div
                                    className='octo-table-cell'
                                    onClick={() => {
                                        this.addCard()
                                    }}
                                >
                                    <FormattedMessage
                                        id='TableComponent.plus-new'
                                        defaultMessage='+ New'
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </div >
            </div >
        )
    }

    private columnWidth(templateId: string): number {
        return Math.max(Constants.minColumnWidth, this.props.boardTree?.activeView?.columnWidths[templateId] || 0)
    }

    private addCard = async (show = false) => {
        const {boardTree} = this.props

        const card = new MutableCard()
        card.parentId = boardTree.board.id
        card.icon = BlockIcons.shared.randomIcon()
        await mutator.insertBlock(
            card,
            'add card',
            async () => {
                if (show) {
                    this.setState({shownCard: card})
                } else {
                    // Focus on this card's title inline on next render
                    this.cardIdToFocusOnRender = card.id
                }
            },
        )
    }

    private async onDropToColumn(template: IPropertyTemplate) {
        const {draggedHeaderTemplate} = this
        if (!draggedHeaderTemplate) {
            return
        }

        const {boardTree} = this.props
        const {board} = boardTree

        Utils.assertValue(mutator)
        Utils.assertValue(boardTree)

        Utils.log(`ondrop. Source column: ${draggedHeaderTemplate.name}, dest column: ${template.name}`)

        // Move template to new index
        const destIndex = template ? board.cardProperties.indexOf(template) : 0
        await mutator.changePropertyTemplateOrder(board, draggedHeaderTemplate, destIndex)
    }
}

export {TableComponent}
