import React, {FormEvent, useEffect, useRef} from 'react'
import {useNavigate, useParams} from 'react-router-dom'
import agent from '../api/agent'
import ObjectID from 'bson-objectid'
import {AccessedBy, Data, Os, SystemRequirements, WindowsOrMacOrLinux,} from '../models/game'
import {controllerValues, playStatusValues, wineIntelValues, yesNo,} from '../componentUtils/SelectValues'
import DeleteDialogue from './DeleteDialogue'
import {Options} from 'react-select'
import stringArrayToSelectObject from '../componentUtils/stringArrayToSelectObject'
import {
    loadingAndContinueState,
    loadingAndSaveState,
    loadingState,
    openAndCloseDeleteDialogue,
} from '../stateManagement/loadingState'
import {useHookstate} from '@hookstate/core'
import {tagState} from '../stateManagement/tagState'
import {retrieveTags} from '../componentUtils/retrieveTags'
import LoadingModal from './LoadingModal'
import {
    Box,
    Button,
    Container,
    FormControl,
    FormLabel,
    Heading,
    InputGroup,
    Spacer,
    Stack,
    Switch,
    Text,
    useColorModeValue,
} from '@chakra-ui/react'
import {CreatableSelect,} from 'chakra-react-select'
import {adminMode} from '../stateManagement/userState'
import {encode} from 'js-base64'
import SingleFieldInput from './formFields/SingleFieldInput'
import TextareaInput from './formFields/TextareaInput'
import SystemRequirementsTextareaInput from './formFields/SystemRequirementsTextareaInput'
import SelectInput from './formFields/SelectInput'
import CreatableSelectInput from './formFields/CreatableSelectInput'
import CheckboxInput from './formFields/CheckboxInput'
import { changeLoadState } from "../stateManagement/loadingState";
import SingleFieldNumericInput from './formFields/SingleFieldNumericInput'

const GameForm = () => {
    const loadState = useHookstate(loadingState)
    const loaded = loadState.get()
    const submitLoadingState = useHookstate(loadingAndSaveState)
    const submitLoading = submitLoadingState.get()
    const history = useNavigate()
    const openState = useHookstate(openAndCloseDeleteDialogue)
    const open = openState.get()
    const moveOnState = useHookstate(loadingAndContinueState)
    const moveOn = moveOnState.get()
    const {id} = useParams<{ id: string }>()
    const tagsState = useHookstate(tagState)
    const tags = tagsState.get()
    const bg = useColorModeValue('gray.50', 'transparent')
    const admin = useHookstate(adminMode).get()

    const gameState = useHookstate<Data>({
        _id: '',
        title: '',
        series: '',
        steamId: '',
        slug: '',
        frontImage: '',
        screenshots: [{id: 0, full: '', thumbnail: ''}],
        genre: [''],
        os: {
            windows: true,
            mac: false,
            linux: false,
            android: false,
            ios: false
        },
        wine: 'Not Tested',
        controller: 'No Controller Support',
        developer: [''],
        publisher: [''],
        releaseDate: '',
        shortDesc: '',
        reviews: '',
        summary: '',
        intel: 'Not Tested',
        steamRating: 0,
        systemRequirements: {
            windows: {
                minimum: '',
                recommended: '',
            },
            mac: {
                minimum: '',
                recommended: '',
            },
            linux: {
                minimum: '',
                recommended: '',
            },
        },
        accessedBy: [{
            user: '',
            store: ['Steam'],
            playStatus: 'Never Played',
            soundtrack: 'No',
            rating: 0,
        }],
        createDate: '',
        lastUpdateDate: '',
        lastModifiedBy: '',
        scrape: true,
    })
    const isMountedRef = useRef(false)
    const game = gameState.get()
    const gameId = game._id

    const getGame = async (id: string, admin: boolean) => {
        changeLoadState(true)
        const result = admin ? await agent.GamesAdmin.details(id) : await agent.Games.details(id)
        return result.data
    }


    useEffect(() => {
        document.title = id ? `Edit ${game.title}` : 'Create Game'
    })

    const getTags = () => {
        if (tags.developer.length < 2) return retrieveTags()
        else return
    }

    useEffect(() => {
        isMountedRef.current = true;

        (async () => {

            changeLoadState(true)
            if (id) getGame(id, admin).then((result) => {
                result.scrape = false
                if (isMountedRef.current) gameState.set(result)
            })

            await getTags()

            changeLoadState(false)
        })()

        return () => {
            isMountedRef.current = false
        }

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [admin])

    const updateGame = async (id: string, data: Data) => {
        try {
            admin ? await agent.GamesAdmin.update(id, data) : await agent.Games.update(id, data)
        } catch (err) {
            return
        }
    }

    const createGame = async (data: Data) => {
        try {
            admin ? await agent.GamesAdmin.create(data) : await agent.Games.create(data)
            return true
        } catch (err) {
            return false
        }
    }

    const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
        event.preventDefault()

        if ((gameId === '' && !game.scrape) || (gameId.length >= 1 && admin)) {
            const shortDesc = game.shortDesc.replace(/"/g, '\'')
            const summary = game.summary.replace(/"/g, '\'')
            const reviews = game.reviews.replace(/"/g, '\'')
            const windowsRecommended = game.systemRequirements.windows.recommended.replace(/"/g, '\'')
            const windowsMinimum = game.systemRequirements.windows.minimum.replace(/"/g, '\'')
            const macMinimum = game.systemRequirements.mac.minimum.replace(/"/g, '\'')
            const macRecommended = game.systemRequirements.mac.recommended.replace(/"/g, '\'')
            const linuxMinimum = game.systemRequirements.linux.minimum.replace(/"/g, '\'')
            const linuxRecommended = game.systemRequirements.linux.recommended.replace(/"/g, '\'')
            gameState.merge({
                shortDesc: encode(shortDesc),
                summary: encode(summary),
                reviews: encode(reviews),
                systemRequirements: {
                    windows: {
                        minimum: encode(windowsMinimum),
                        recommended: encode(windowsRecommended),
                    },
                    mac: {
                        minimum: encode(macMinimum),
                        recommended: encode(macRecommended),
                    },
                    linux: {
                        minimum: encode(linuxMinimum),
                        recommended: encode(linuxRecommended),
                    },
                },
            })
        }


        //@ts-ignore
        if (event.nativeEvent.submitter.dataset.name === 'save-and-view') {
            submitLoadingState.set(false)
            moveOnState.set(true)
        }

        //@ts-ignore
        if (event.nativeEvent.submitter.dataset.name === 'save') {
            moveOnState.set(false)
            submitLoadingState.set(true)
        }

        if (gameId.length > 1) {
            // @ts-ignore
            updateGame(game._id, game).then(async () => {
                changeLoadState(true)
                await retrieveTags()
                // @ts-ignore
                if (event.nativeEvent.submitter.dataset.name === 'save-and-view') {
                    moveOnState.set(() => false)
                    changeLoadState(false)
                    return history(`/games/${gameId}`)
                }
                await getGame(gameId, admin)
                changeLoadState(false)
            })
            // if (submitLoading) submitLoadingState.set(false)
        } else {
            const id = new ObjectID().toHexString()
            const newGame = {
                ...game,
                _id: id,
            }

            // @ts-ignore
            createGame(newGame).then(async (data) => {
                    if (!data) {
                        changeLoadState(false)
                        return
                        // @ts-ignore
                    } else if (event.nativeEvent.submitter.dataset.name === 'save') {
                        changeLoadState(false)
                        return history(`/games/${newGame._id}/edit`)
                        // @ts-ignore
                    } else if (event.nativeEvent.submitter.dataset.name === 'save-and-view') {
                        changeLoadState(false)
                        return history(`/games/${newGame._id}`)
                    }
                },
            )
        }
    }

    const handleInputChange = <G extends keyof Data>(name: string, value: Data[G]) => {
        gameState.merge(() => ({[name]: value}))
    }

    const handleToggle = <G extends keyof Data>(name: G) => {
        gameState.merge(() => ({[name]: !game[name]}))
    }

    const handleOSChange = <O extends keyof Os>(name: O) => {
        gameState.os.merge(() => ({
            [name]: !game.os[name],
        }))
    }

    const handleSystemRequirements = <S extends keyof SystemRequirements,
        WML extends keyof WindowsOrMacOrLinux>(
        name: S,
        minRec: WML,
        value: string,
    ) => {
        gameState.systemRequirements[name].merge(() => ({
            [minRec]: value,
        }))
    }

    const handleAccessedBy = <A extends keyof AccessedBy>(name: A, value: AccessedBy[A]) => {
        gameState.accessedBy[0].merge(() => ({[name]: value}))
    }

    // @ts-ignore
    const genres = stringArrayToSelectObject(game.genre)
    // @ts-ignore
    const store = stringArrayToSelectObject(game.accessedBy[0].store)
    // @ts-ignore
    const developer = stringArrayToSelectObject(game.developer)
    // @ts-ignore
    const publisher = stringArrayToSelectObject(game.publisher)
    const screenshots = stringArrayToSelectObject(
        game.screenshots.map((screenshot) => screenshot.full),
    )

    if (loaded) return <LoadingModal/>

    if (open) return <DeleteDialogue
        //@ts-ignore
        id={id}
        open={open}
        setOpen={openState.set}
        setLoading={loadState.set}
        //@ts-ignore
        game={game}
    />

    return (
        <>
            <Container maxW="container.md" mt="10">
                {id ? <Heading as="h1" size="xl">Editing {game.title}</Heading> :
                    <Heading as="h1" size="xl">Add a Game to the Database</Heading>}
                <form onSubmit={handleSubmit} style={{marginTop: '2rem'}}>
                    <Stack spacing={[1, 5]} direction={{base: 'column', sm: 'row'}} justify={'flex-end'}>
                        <Button
                            isLoading={submitLoading}
                            colorScheme="blue"
                            type="submit"
                            data-name={"save"}
                        >
                            Save
                        </Button>
                        <Button
                            isLoading={moveOn}
                            colorScheme="green"
                            type="submit"
                            data-name={"save-and-view"}
                        >
                            Save and View
                        </Button>
                    </Stack>
                    <InputGroup>
                        {!id || admin ? (
                            <SingleFieldNumericInput label={'Steam ID'} textField={'steamId'} field={game.steamId} bg={bg}
                                              handleInputChange={handleInputChange} required={game.scrape}/>
                        ) : ''}
                        {!id && (
                            <FormControl display="flex" ml={'3'} alignItems="center">
                                <FormLabel htmlFor="scrape" mb="0">Steam Scrape</FormLabel>
                                <Switch
                                    isChecked={game.scrape}
                                    onChange={() => {
                                        handleToggle('scrape')
                                    }}
                                    name="scrape"
                                    id="scrape"
                                />
                            </FormControl>
                        )}
                    </InputGroup>
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1 && admin) ? (
                        <SingleFieldInput label={'Title'} textField={'title'} field={game.title} bg={bg}
                                          handleInputChange={handleInputChange} required={false}/>
                    ) : ''}
                    <>
                        <FormControl mb="3">
                            <FormLabel>Series</FormLabel>
                            <Box
                                bg={bg}
                                border="1px"
                                borderColor="gray.500"
                                rounded="md">
                                <CreatableSelect
                                    name="series"
                                    isClearable
                                    onChange={(values) => {
                                        if (values === null) values = {value: '', label: ''}
                                        if (values) {
                                            handleInputChange('series', values.value)
                                        }
                                    }}
                                    options={tags.series}
                                    value={{value: game.series, label: game.series}}
                                />
                            </Box>
                        </FormControl>
                    </>
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1 && admin) ? (
                        <>
                            <SingleFieldInput label={'Front Image'} textField={'frontImage'} field={game.frontImage}
                                              bg={bg}
                                              handleInputChange={handleInputChange} required={false}/>
                            <FormControl mb="3">
                                <FormLabel>Screenshots</FormLabel>
                                <Box
                                    bg={bg}
                                    border="1px"
                                    borderColor="gray.500"
                                    rounded="md">
                                    <CreatableSelect
                                        name="screenshots"
                                        isClearable
                                        isMulti
                                        onChange={(
                                            values: Options<{
                                                value: string
                                                label: string
                                            }>,
                                        ) => {
                                            if (values) {
                                                const valueArray = values
                                                    .reduce(
                                                        (
                                                            prev: {
                                                                id: number
                                                                full: string
                                                                thumbnail: string
                                                            }[],
                                                            curr,
                                                            index,
                                                        ) => {
                                                            prev.push({
                                                                id: index,
                                                                full: curr.value,
                                                                thumbnail: curr.value,
                                                            })
                                                            return prev
                                                        },
                                                        [],
                                                    )
                                                    .filter((x) => x.full !== 'Copy/Paste Image URL and Press Enter')
                                                handleInputChange('screenshots', valueArray)
                                            }
                                        }}
                                        placeholder="Copy/Paste Image URL and Press Enter"
                                        value={screenshots[0]?.value === '' ? [] : screenshots}
                                    />
                                </Box>
                            </FormControl>
                        </>
                    ) : ''}
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1) ?
                        <CreatableSelectInput label={'Genres'} textField={'genre'}
                                              value={genres[0]?.value === '' ? [] : genres}
                                              bg={bg}
                                              handleInputChange={handleInputChange} options={tags.genres}/>
                        : ''}
                    <>
                        <CreatableSelectInput label={'Store'} textField={'store'}
                                              value={store[0]?.value === '' ? [] : store}
                                              bg={bg}
                                              handleInputChange={handleAccessedBy} options={tags.store}/>
                        <SelectInput label={'Play Status'} textField={'playStatus'}
                                     field={game.accessedBy[0].playStatus} bg={bg}
                                     handleAccessedBy={handleAccessedBy} options={playStatusValues}/>
                    </>
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1 && admin) ? (
                        <Box mt="5" mb="5">
                            <FormLabel>Operating Systems</FormLabel>
                            <Box
                                bg={bg}
                                border="1px"
                                borderColor="gray.500"
                                rounded="md"
                                p="2">
                                <Stack spacing={[1, 5]} direction={['column', 'row']}>
                                    <CheckboxInput label={'Windows'} textField={'windows'} checked={game.os.windows}
                                                   defaultIsChecked={true} handleOSChange={handleOSChange}/>
                                    <CheckboxInput label={'Mac OSX'} textField={'mac'} checked={game.os.mac}
                                                   defaultIsChecked={false}
                                                   handleOSChange={handleOSChange}/>
                                    <CheckboxInput label={'Linux'} textField={'linux'} checked={game.os.linux}
                                                   defaultIsChecked={false}
                                                   handleOSChange={handleOSChange}/>
                                    <CheckboxInput label={'Android'} textField={'android'} checked={game.os.android}
                                                   defaultIsChecked={false}
                                                   handleOSChange={handleOSChange}/>
                                    <CheckboxInput label={'iOS'} textField={'ios'} checked={game.os.ios}
                                                   defaultIsChecked={false}
                                                   handleOSChange={handleOSChange}/>
                                </Stack>
                            </Box>
                        </Box>
                    ) : ''}
                    <>
                        <SelectInput label={'Does it work with WINE?'} textField={'wine'} field={game.wine} bg={bg}
                                     handleAccessedBy={handleInputChange} options={wineIntelValues}/>
                        <SelectInput label={'Does it work on an Intel Graphics Card?'} textField={'intel'}
                                     field={game.intel}
                                     bg={bg}
                                     handleAccessedBy={handleInputChange} options={wineIntelValues}/>
                    </>
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1 && admin) ? (
                        <>
                            <SelectInput label={'Does it support a controller?'} textField={'controller'}
                                         field={game.controller}
                                         bg={bg}
                                         handleAccessedBy={handleInputChange} options={controllerValues}/>
                            <CreatableSelectInput label={'Developers'} textField={'developer'}
                                                  value={developer[0]?.value === '' ? [] : developer} bg={bg}
                                                  handleInputChange={handleInputChange} options={tags.developer}/>
                            <CreatableSelectInput label={'Publishers'} textField={'publisher'}
                                                  value={publisher[0]?.value === '' ? [] : publisher} bg={bg}
                                                  handleInputChange={handleInputChange} options={tags.publisher}/>
                            <SingleFieldInput label={'Release Date'} textField={'releaseDate'} field={game.releaseDate}
                                              bg={bg}
                                              handleInputChange={handleInputChange} required={game.scrape}/>
                            <TextareaInput label={'Short Description'} textField={'shortDesc'} field={game.shortDesc}
                                           bg={bg}
                                           handleInputChange={handleInputChange} rows={3}/>
                            <TextareaInput label={'Reviews'} textField={'reviews'} field={game.reviews} bg={bg}
                                           handleInputChange={handleInputChange} rows={5}/>
                            <TextareaInput label={'Summary'} textField={'summary'} field={game.summary} bg={bg}
                                           handleInputChange={handleInputChange} rows={5}/>
                        </>
                    ) : ''}
                    <>
                        <SelectInput label={'Do you own the Soundtrack?'} textField={'soundtrack'}
                                     field={game.accessedBy[0].soundtrack} bg={bg}
                                     handleAccessedBy={handleAccessedBy} options={yesNo}/>
                    </>
                    {(gameId === '' && !game.scrape) || (gameId.length >= 1 && admin) ? (
                        <>
                            <Text align="center" fontSize="xl" mt="10">System Requirements</Text>
                            <Box>
                                <Text fontSize="lg" align="center">Windows</Text>
                                <SystemRequirementsTextareaInput label={'Windows Minimum'}
                                                                 textField={'systemRequirements.windows.minimum'}
                                                                 field={game.systemRequirements.windows.minimum} bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                                <SystemRequirementsTextareaInput label={'Windows Recommended'}
                                                                 textField={'systemRequirements.windows.recommended'}
                                                                 field={game.systemRequirements.windows.recommended}
                                                                 bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                                <Text fontSize="lg" align="center">Mac OSX</Text>
                                <SystemRequirementsTextareaInput label={'Mac OSX Minimum'}
                                                                 textField={'systemRequirements.mac.minimum'}
                                                                 field={game.systemRequirements.mac.minimum} bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                                <SystemRequirementsTextareaInput label={'Mac OSX Recommended'}
                                                                 textField={'systemRequirements.mac.recommended'}
                                                                 field={game.systemRequirements.mac.recommended} bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                                <Text fontSize="lg" align="center">Linux</Text>
                                <SystemRequirementsTextareaInput label={'Linux Minimum'}
                                                                 textField={'systemRequirements.linux.minimum'}
                                                                 field={game.systemRequirements.linux.minimum} bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                                <SystemRequirementsTextareaInput label={'Linux Recommended'}
                                                                 textField={'systemRequirements.linux.recommended'}
                                                                 field={game.systemRequirements.linux.recommended}
                                                                 bg={bg}
                                                                 handleSystemRequirements={handleSystemRequirements}
                                                                 rows={3}/>
                            </Box>
                        </>
                    ) : ''}
                    <Stack spacing={[1, 5]} direction={{base: 'column', sm: 'row'}} align={{base: 'start'}}>
                        <Button
                            isLoading={submitLoading}
                            colorScheme="blue"
                            type="submit"
                            data-name={"save"}
                        >
                            Save
                        </Button>
                        <Button
                            isLoading={moveOn}
                            colorScheme="green"
                            type="submit"
                            data-name={"save-and-view"}
                        >
                            Save and View
                        </Button>
                        {id && (
                            <>
                                <Spacer/>
                                <Button
                                    colorScheme="red"
                                    onMouseDown={() => {
                                        openState.set(true)
                                    }}
                                >
                                    Delete
                                </Button>
                            </>
                        )}
                    </Stack>
                </form>
            </Container>
        </>
    )
}

export default GameForm
