// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react'
import {Provider as ReduxProvider} from 'react-redux'
import {render, screen} from '@testing-library/react'
import configureStore from 'redux-mock-store'
import '@testing-library/jest-dom'
import userEvents from '@testing-library/user-event'

import 'isomorphic-fetch'
import {mocked} from 'jest-mock'

import {TestBlockFactory} from '../../test/testBlockFactory'
import {FetchMock} from '../../test/fetchMock'
import {BoardView} from '../../blocks/boardView'

import {IUser} from '../../user'

import {Utils, IDType} from '../../utils'

import {wrapDNDIntl} from '../../testUtils'

import Mutator from '../../mutator'

import Table from './table'

global.fetch = FetchMock.fn

beforeEach(() => {
    FetchMock.fn.mockReset()
})

jest.mock('../../mutator')
jest.mock('../../utils')
jest.mock('../../telemetry/telemetryClient')
const mockedMutator = mocked(Mutator, true)

describe('components/table/Table', () => {
    const board = TestBlockFactory.createBoard()
    const view = TestBlockFactory.createBoardView(board)
    view.fields.viewType = 'table'
    view.fields.groupById = undefined
    view.fields.visiblePropertyIds = ['property1', 'property2']

    const view2 = TestBlockFactory.createBoardView(board)
    view2.fields.sortOptions = []

    const card = TestBlockFactory.createCard(board)
    const cardTemplate = TestBlockFactory.createCard(board)
    cardTemplate.fields.isTemplate = true

    const state = {
        users: {
            boardUsers: {
                'user-id-1': {username: 'username_1'} as IUser,
                'user-id-2': {username: 'username_2'} as IUser,
                'user-id-3': {username: 'username_3'} as IUser,
                'user-id-4': {username: 'username_4'} as IUser,
            },
        },
        comments: {
            comments: {},
            commentsByCard: {},
        },
        contents: {
            contents: {},
            contentsByCard: {},
        },
        cards: {
            cards: {
                [card.id]: card,
            },
        },
        teams: {
            current: {id: 'team-id'},
        },
        boards: {
            current: board.id,
            boards: {
                [board.id]: board,
            },
            myBoardMemberships: {
                [board.id]: {userId: 'user_id_1', schemeAdmin: true},
            },
        },
    }

    test('should match snapshot', async () => {
        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore(state)

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card]}
                    views={[view, view2]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot without permissions', async () => {
        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore({...state, teams: {current: undefined}})

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card]}
                    views={[view, view2]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot, read-only', async () => {
        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore(state)

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card]}
                    views={[view, view2]}
                    selectedCardIds={[]}
                    readonly={true}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )

        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot with GroupBy', async () => {
        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore(state)

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={{...view, fields: {...view.fields, groupById: 'property1'}} as BoardView}
                    visibleGroups={[{option: {id: '', value: 'test', color: ''}, cards: []}]}
                    groupByProperty={{
                        id: '',
                        name: 'Property 1',
                        type: 'text',
                        options: [{id: 'property1', value: 'Property 1', color: ''}],
                    }}
                    cards={[card]}
                    views={[view, view2]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('limited card in table view', () => {
        const callback = jest.fn()
        const addCard = jest.fn()
        const boardTest = TestBlockFactory.createBoard()
        const card1 = TestBlockFactory.createCard(boardTest)
        const card2 = TestBlockFactory.createCard(boardTest)
        const mockStore = configureStore([])

        const stateTest = {
            comments: {
                comments: {},
            },
            contents: {
                contents: {},
            },
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
            teams: {
                current: {id: 'team-id'},
            },
            boards: {
                current: boardTest.id,
                boards: {
                    [boardTest.id]: boardTest,
                },
                myBoardMemberships: {
                    [boardTest.id]: {userId: 'user_id_1', schemeAdmin: true},
                },
            },
        }

        const storeTest = mockStore(stateTest)
        card.limited = true

        const component = wrapDNDIntl(
            <ReduxProvider store={storeTest}>
                <Table
                    board={boardTest}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view, view2]}
                    selectedCardIds={[]}
                    readonly={true}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={2}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container, getByTitle} = render(component)
        expect(getByTitle('hidden-card-count')).toHaveTextContent('2')
        expect(container).toMatchSnapshot()
    })
})

describe('components/table/Table extended', () => {
    const state = {
        users: {
            boardUsers: {
                'user-id-1': {username: 'username_1'} as IUser,
                'user-id-2': {username: 'username_2'} as IUser,
                'user-id-3': {username: 'username_3'} as IUser,
                'user-id-4': {username: 'username_4'} as IUser,
            },
        },
        comments: {
            comments: {},
            commentsByCard: {},
        },
        contents: {
            contents: {},
            contentsByCard: {},
        },
        cards: {
            cards: {},
        },
        teams: {
            current: {id: 'team-id'},
        },
        boards: {
            current: 'board_id',
            boards: {
                board_id: {id: 'board_id'},
            },
            myBoardMemberships: {
                board_id: {userId: 'user_id_1', schemeAdmin: true},
            },
        },
        clientConfig: {
            value: {
                teammateNameDisplay: 'username',
            },
        },
    }

    test('should match snapshot with CreatedAt', async () => {
        const board = TestBlockFactory.createBoard()

        const dateCreatedId = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: dateCreatedId,
            name: 'Date Created',
            type: 'createdTime',
            options: [],
        })

        const card1 = TestBlockFactory.createCard(board)
        card1.createAt = Date.parse('15 Jun 2021 16:22:00')

        const card2 = TestBlockFactory.createCard(board)
        card2.createAt = Date.parse('15 Jun 2021 16:22:00')

        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', dateCreatedId]

        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
            teams: {
                current: {id: 'team-id'},
            },
            boards: {
                current: board.id,
                boards: {
                    [board.id]: board,
                },
                myBoardMemberships: {
                    [board.id]: {userId: 'user_id_1', schemeAdmin: true},
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot with UpdatedAt', async () => {
        const board = TestBlockFactory.createBoard()
        const dateUpdatedId = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: dateUpdatedId,
            name: 'Date Updated',
            type: 'updatedTime',
            options: [],
        })

        const card1 = TestBlockFactory.createCard(board)
        card1.updateAt = Date.parse('20 Jun 2021 12:22:00')

        const card2 = TestBlockFactory.createCard(board)
        card2.updateAt = Date.parse('20 Jun 2021 12:22:00')

        const card2Comment = TestBlockFactory.createCard(board)
        card2Comment.parentId = card2.id
        card2Comment.type = 'comment'
        card2Comment.updateAt = Date.parse('21 Jun 2021 15:23:00')

        const card2Text = TestBlockFactory.createCard(board)
        card2Text.parentId = card2.id
        card2Text.type = 'text'
        card2Text.updateAt = Date.parse('22 Jun 2021 11:23:00')

        card2.fields.contentOrder = [card2Text.id]

        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', dateUpdatedId]

        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            comments: {
                comments: {
                    [card2Comment.id]: card2Comment,
                },
                commentsByCard: {
                    [card2.id]: [card2Comment],
                },
            },
            contents: {
                contents: {
                    [card2Text.id]: card2Text,
                },
                contentsByCard: {
                    [card2.id]: [card2Text],
                },
            },
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )
        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot with CreatedBy', async () => {
        const board = TestBlockFactory.createBoard()

        const createdById = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: createdById,
            name: 'Created By',
            type: 'createdBy',
            options: [],
        })

        const card1 = TestBlockFactory.createCard(board)
        card1.createdBy = 'user-id-1'

        const card2 = TestBlockFactory.createCard(board)
        card2.createdBy = 'user-id-2'

        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', createdById]

        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )

        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should match snapshot with UpdatedBy', async () => {
        const board = TestBlockFactory.createBoard()

        const modifiedById = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: modifiedById,
            name: 'Last Modified By',
            type: 'updatedBy',
            options: [],
        })

        const card1 = TestBlockFactory.createCard(board)
        card1.modifiedBy = 'user-id-1'
        card1.updateAt = Date.parse('15 Jun 2021 16:22:00')

        const card1Text = TestBlockFactory.createCard(board)
        card1Text.parentId = card1.id
        card1Text.type = 'text'
        card1Text.modifiedBy = 'user-id-4'
        card1Text.updateAt = Date.parse('16 Jun 2021 16:22:00')

        card1.fields.contentOrder = [card1Text.id]

        const card2 = TestBlockFactory.createCard(board)
        card2.modifiedBy = 'user-id-2'
        card2.updateAt = Date.parse('15 Jun 2021 16:22:00')

        const card2Comment = TestBlockFactory.createCard(board)
        card2Comment.parentId = card2.id
        card2Comment.type = 'comment'
        card2Comment.modifiedBy = 'user-id-3'
        card2.updateAt = Date.parse('16 Jun 2021 16:22:00')

        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', modifiedById]

        const callback = jest.fn()
        const addCard = jest.fn()

        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            comments: {
                comments: {
                    [card2Comment.id]: card2Comment,
                },
                commentsByCard: {
                    [card2.id]: [card2Comment],
                },
            },
            contents: {
                contents: {
                    [card1Text.id]: card1Text,
                },
                contentsByCard: {
                    [card1.id]: [card1Text],
                },
            },
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={callback}
                    addCard={addCard}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )

        const {container} = render(component)
        expect(container).toMatchSnapshot()
    })

    test('should delete snapshot', async () => {
        const board = TestBlockFactory.createBoard()

        const modifiedById = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: modifiedById,
            name: 'Last Modified By',
            type: 'updatedBy',
            options: [],
        })
        const card1 = TestBlockFactory.createCard(board)
        card1.title = 'card1'
        const card2 = TestBlockFactory.createCard(board)
        card2.title = 'card2'
        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', modifiedById]
        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={jest.fn()}
                    addCard={jest.fn()}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )

        const {getByTitle, getByRole, getAllByTitle} = render(component)
        const card1Name = getByTitle(card1.title)
        userEvents.hover(card1Name)
        const menuBtn = getAllByTitle('MenuBtn')
        userEvents.click(menuBtn[0])
        const deleteBtn = getByRole('button', {name: 'Delete'})
        userEvents.click(deleteBtn)
        const dailogDeleteBtn = screen.getByRole('button', {name: 'Delete'})
        userEvents.click(dailogDeleteBtn)
        expect(mockedMutator.deleteBlock).toBeCalledTimes(1)
    })

    test('should have Duplicate Button', async () => {
        const board = TestBlockFactory.createBoard()

        const modifiedById = Utils.createGuid(IDType.User)
        board.cardProperties.push({
            id: modifiedById,
            name: 'Last Modified By',
            type: 'updatedBy',
            options: [],
        })
        const card1 = TestBlockFactory.createCard(board)
        card1.title = 'card1'
        const card2 = TestBlockFactory.createCard(board)
        card2.title = 'card2'
        const view = TestBlockFactory.createBoardView(board)
        view.fields.viewType = 'table'
        view.fields.groupById = undefined
        view.fields.visiblePropertyIds = ['property1', 'property2', modifiedById]
        const mockStore = configureStore([])
        const store = mockStore({
            ...state,
            cards: {
                cards: {
                    [card1.id]: card1,
                    [card2.id]: card2,
                },
            },
        })

        const component = wrapDNDIntl(
            <ReduxProvider store={store}>
                <Table
                    board={board}
                    activeView={view}
                    visibleGroups={[]}
                    cards={[card1, card2]}
                    views={[view]}
                    selectedCardIds={[]}
                    readonly={false}
                    cardIdToFocusOnRender=''
                    showCard={jest.fn()}
                    addCard={jest.fn()}
                    onCardClicked={jest.fn()}
                    hiddenCardsCount={0}
                    showHiddenCardCountNotification={jest.fn()}
                />
            </ReduxProvider>,
        )

        const {getByTitle, getByRole, getAllByTitle, container} = render(component)
        const card1Name = getByTitle(card1.title)
        userEvents.hover(card1Name)
        const menuBtn = getAllByTitle('MenuBtn')
        userEvents.click(menuBtn[0])
        const duplicateBtn = getByRole('button', {name: 'Duplicate'})
        expect(duplicateBtn).not.toBe(null)
        userEvents.click(duplicateBtn)
        expect(mockedMutator.duplicateCard).toBeCalledTimes(1)
        expect(container).toMatchSnapshot()
    })
})
