import Vue from 'vue'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import MorphToField from '@/components/Form/MorphToField'
import flushPromises from 'flush-promises'

jest.mock('@/storage/MorphToFieldStorage')

describe('MorphToField', () => {
    /**
     * Note: This field doesn't support loading all resources initially unless we're editing or
     * coming in via a related resource because otherwise we don't know which type to load them for
     */
    test('when creating it has the correct initial state if its not searchable', async () => {
        const wrapper = shallowMount(MorphToField, {
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: '',
                    morphToType: '',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: '',
                viaResourceId: '',
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(false)
        expect(wrapper.vm.editingExistingResource).toBe(false)
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.resourceType).toBe('')
        expect(wrapper.vm.isSearchable).toBe(false)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(false)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: '',
                current: null,
                first: false,
                search: '',
                withTrashed: false,
            },
        })
        expect(wrapper.vm.availableResources).toEqual([])
        expect(wrapper.vm.selectedResource).toEqual(null)
        expect(wrapper.vm.selectedResourceId).toBe(null)
    })

    test('when creating it has the correct initial state if it is searchable', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: true,
                    morphToRelationship: 'commentable',
                    morphToId: '',
                    morphToType: '',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: '',
                viaResourceId: '',
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(false)
        expect(wrapper.vm.editingExistingResource).toBe(false)
        expect(wrapper.vm.resourceType).toBe('')
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.isSearchable).toBe(true)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(false)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: '',
                current: null,
                first: false,
                search: '',
                withTrashed: false,
            },
        })
        expect(wrapper.vm.availableResources).toEqual([])
        expect(wrapper.vm.selectedResource).toEqual(null)
        expect(wrapper.vm.selectedResourceId).toBe(null)
    })

    test('when creating via a related resource and is not searchable it loads the related type all related resources and selects the related resource', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: '',
                    morphToType: '',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(true)
        expect(wrapper.vm.editingExistingResource).toBe(false)
        expect(wrapper.vm.resourceType).toBe('posts')
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.isSearchable).toBe(false)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(true)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: 'posts',
                current: 1,
                first: false,
                search: '',
                withTrashed: false,
            },
        })

        await flushPromises()

        expect(wrapper.vm.availableResources).toEqual([{ value: 1 }, { value: 2 }, { value: 3 }])
        expect(wrapper.vm.softDeletes).toBe(true)
        expect(wrapper.vm.selectedResource).toEqual({ value: 1 })
        expect(wrapper.vm.selectedResourceId).toBe(1)
    })

    test('when creating via a related resource and it is searchable, it only loads the related type and selects the related resource', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: true,
                    morphToRelationship: 'commentable',
                    morphToId: '',
                    morphToType: '',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(true)
        expect(wrapper.vm.editingExistingResource).toBe(false)
        expect(wrapper.vm.resourceType).toBe('posts')
        expect(wrapper.vm.isSearchable).toBe(true)
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(true)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: 'posts',
                current: 1,
                first: true,
                search: '',
                withTrashed: false,
            },
        })

        await flushPromises()

        expect(wrapper.vm.availableResources).toEqual([{ value: 1 }])
        expect(wrapper.vm.softDeletes).toBe(true)
        expect(wrapper.vm.selectedResource).toEqual({ value: 1 })
        expect(wrapper.vm.selectedResourceId).toBe(1)
    })

    test('when editing an existing resource and the field is not searchable it selects the related resource type and resource and loads all resources', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: 1,
                    morphToType: 'posts',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: '',
                viaResourceId: null,
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(true)
        expect(wrapper.vm.editingExistingResource).toBe(true)
        expect(wrapper.vm.resourceType).toBe('posts')
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.isSearchable).toBe(false)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(true)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: 'posts',
                current: 1,
                first: false,
                search: '',
                withTrashed: false,
            },
        })

        await flushPromises()

        expect(wrapper.vm.availableResources).toEqual([{ value: 1 }, { value: 2 }, { value: 3 }])
        expect(wrapper.vm.softDeletes).toBe(true)
        expect(wrapper.vm.selectedResource).toEqual({ value: 1 })
        expect(wrapper.vm.selectedResourceId).toBe(1)
    })

    test('when editing an existing resource and the field is searchable it selects the related resource type and resource and doesnt load all the resources', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: true,
                    morphToRelationship: 'commentable',
                    morphToId: 1,
                    morphToType: 'posts',
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: '',
                viaResourceId: null,
            },
        })

        expect(wrapper.vm.initializingWithExistingResource).toBe(true)
        expect(wrapper.vm.editingExistingResource).toBe(true)
        expect(wrapper.vm.resourceType).toBe('posts')
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.isSearchable).toBe(true)
        expect(wrapper.vm.shouldSelectInitialResource).toBe(true)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                type: 'posts',
                current: 1,
                first: true,
                search: '',
                withTrashed: false,
            },
        })

        await flushPromises()

        expect(wrapper.vm.availableResources).toEqual([{ value: 1 }])
        expect(wrapper.vm.softDeletes).toBe(true)
        expect(wrapper.vm.selectedResource).toEqual({ value: 1 })
        expect(wrapper.vm.selectedResourceId).toBe(1)
    })

    test('it determines if its related resource soft deletes', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                    // resourceName: 'users',
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        expect(wrapper.vm.softDeletes).toBe(false)

        await flushPromises()

        expect(wrapper.vm.softDeletes).toBe(true)
    })

    test('including trashed resources in the available resources can be enabled when a resource supports soft-deleting', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        wrapper.vm.enableWithTrashed()

        await flushPromises()

        expect(wrapper.vm.softDeletes).toBe(true)
        expect(wrapper.vm.withTrashed).toBe(true)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                current: 1,
                first: false,
                search: '',
                type: 'posts',
                withTrashed: true,
            },
        })
    })

    test('including trashed resources in the available resources cannot be enabled when a resource doesnt support soft-deleting', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'videos',
                viaResourceId: 1,
            },
        })

        await flushPromises()

        expect(wrapper.vm.softDeletes).toBe(false)
        expect(wrapper.vm.withTrashed).toBe(false)
        expect(wrapper.vm.queryParams).toEqual({
            params: {
                current: 1,
                first: false,
                search: '',
                type: 'videos',
                withTrashed: false,
            },
        })
    })

    test('including trashed resources in the available resources is disabled by default', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'videos',
                viaResourceId: 1,
            },
        })

        await flushPromises()

        expect(wrapper.vm.withTrashed).toBe(false)
    })

    test('it determines if its related resource doesnt soft delete', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        await flushPromises()

        expect(wrapper.vm.softDeletes).toBe(true)

        wrapper.setData({ resourceType: 'videos' })
        wrapper.vm.determineIfSoftDeletes()

        await flushPromises()

        expect(wrapper.vm.softDeletes).toBe(false)
    })

    test('it correctly handles filling the formData object for submit', async () => {
        const wrapper = shallowMount(MorphToField, {
            stubs: ['default-field'],
            propsData: {
                field: {
                    name: 'Commentable',
                    attribute: 'commentable',
                    searchable: false,
                    morphToRelationship: 'commentable',
                    morphToId: null,
                    morphToType: null,
                    morphToTypes: [
                        {
                            display: 'Post',
                            type: 'App\\Nova\\Post',
                            value: 'posts',
                        },
                        {
                            display: 'Video',
                            type: 'App\\Nova\\Video',
                            value: 'videos',
                        },
                    ],
                },
                resourceName: '',
                viaRelationship: '',
                viaResource: 'posts',
                viaResourceId: 1,
            },
        })

        await flushPromises()

        const expectedFormData = new FormData()
        expectedFormData.append('commentable', 1)
        expectedFormData.append('commentable_type', 'posts')
        expectedFormData.append('commentable_trashed', false)

        const formData = new FormData()

        wrapper.vm.field.fill(formData)

        expect(formData).toEqual(expectedFormData)
    })
})
