import React from 'react'
import type { GridPreferencesAdapter } from '../types'
import { render } from '../utils/test-utils'
import { useLocalStoragePreferences } from './use-local-storage-preferences'

const TestPreferences = ({
    version,
    testRef,
}: {
    version?: string
    testRef: React.Ref<GridPreferencesAdapter>
}) => {
    const adapter = useLocalStoragePreferences('test-key', version)

    React.useImperativeHandle(testRef, () => adapter)

    return <div />
}

function renderWithRef({ version }: { version?: string } = {}) {
    const ref = React.createRef<GridPreferencesAdapter>()
    render(<TestPreferences testRef={ref} version={version} />)
    return ref.current!
}

beforeEach(() => {
    window.localStorage.clear()
})

const preferences = {
    columns: [{ id: '123', width: 279 }],
}

describe('useLocalStoragePreferences', () => {
    describe('loading', () => {
        describe('when localStorage is disabled', () => {
            let getItem: typeof window.localStorage.getItem

            beforeEach(() => {
                getItem = window.localStorage.getItem
            })

            afterEach(() => {
                window.localStorage.getItem = getItem
            })

            it('should resolve with nothing', async () => {
                window.localStorage.getItem = jest
                    .fn()
                    .mockImplementationOnce(() => {
                        throw new Error()
                    })

                const adapter = renderWithRef({ version: 'v2' })

                expect(await adapter.load()).toBeUndefined()
            })
        })
        describe('when not previously saved', () => {
            it('should resolve with nothing', async () => {
                const adapter = renderWithRef({ version: 'v2' })

                expect(await adapter.load()).toBeUndefined()
            })
        })
        describe('when version does not match', () => {
            it('should resolve with nothing', async () => {
                window.localStorage.setItem(
                    'test-key',
                    JSON.stringify({
                        version: 'v1',
                        prefs: preferences,
                    })
                )

                const adapter = renderWithRef({ version: 'v2' })

                expect(await adapter.load()).toBeUndefined()
            })
        })
        describe('when content is malformed', () => {
            it('should resolve with nothing (bad prefs)', async () => {
                window.localStorage.setItem(
                    'test-key',
                    JSON.stringify({
                        version: 'v1',
                        prefs: {
                            thisIsWrong: true,
                        },
                    })
                )

                const adapter = renderWithRef()

                expect(await adapter.load()).toBeUndefined()
            })

            it('should resolve with nothing (bad columns array)', async () => {
                window.localStorage.setItem(
                    'test-key',
                    JSON.stringify({
                        version: 'v1',
                        prefs: {
                            columns: { wrong: true },
                        },
                    })
                )

                const adapter = renderWithRef()

                expect(await adapter.load()).toBeUndefined()
            })

            it('should resolve with nothing (bad column values)', async () => {
                window.localStorage.setItem(
                    'test-key',
                    JSON.stringify({
                        version: 'v1',
                        prefs: {
                            columns: [{ id: '123', wrong: true }],
                        },
                    })
                )

                const adapter = renderWithRef()

                expect(await adapter.load()).toBeUndefined()
            })
        })
        describe('when valid', () => {
            it('should return the payload', async () => {
                window.localStorage.setItem(
                    'test-key',
                    JSON.stringify({
                        version: 'v1',
                        prefs: preferences,
                    })
                )

                const adapter = renderWithRef()

                expect(await adapter.load()).toEqual(preferences)
            })
        })
    })

    describe('saving', () => {
        describe('when localStorage is disabled', () => {
            let getItem: typeof window.localStorage.getItem

            beforeEach(() => {
                getItem = window.localStorage.getItem
            })

            afterEach(() => {
                window.localStorage.getItem = getItem
            })

            it('should not throw an error', () => {
                window.localStorage.setItem = jest
                    .fn()
                    .mockImplementationOnce(() => {
                        throw new Error()
                    })

                const adapter = renderWithRef()
                expect(() => {
                    adapter.save(preferences)
                }).not.toThrow()
            })
        })

        describe('when localStorage is enabled', () => {
            it('should persist the preferences and version', () => {
                const adapter = renderWithRef()
                adapter.save(preferences)
                const savedVal = window.localStorage.getItem('test-key')!
                expect(JSON.parse(savedVal)).toEqual({
                    version: 'v1',
                    prefs: preferences,
                })
            })
        })
    })
})
