import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { ChangeEventHandler, FormEventHandler, PropsWithChildren, useEffect, useState } from 'react'
import { ApiError } from '../../api/ApiError'
import getRecipe from '../../api/getRecipe'
import UpdateRecipeSpec, { fromFullRecipeView } from '../../api/types/UpdateRecipeSpec'
import updateRecipe from '../../api/updateRecipe'
import { useAuth } from '../../AuthProvider'
import { useRefresh } from '../../RefreshProvider'
import classes from './edit-recipe.module.css'

interface ControlProps {
    id: string
    displayName: string
}

const Control = ({ id, displayName, children }: PropsWithChildren<ControlProps>) => {
    return (
        <div className={classes.control}>
            <label htmlFor={id}>{displayName}</label>
            {children}
        </div>
    )
}

type TextInputProps = {
    id: string
} & (NullableTextInputProps | NonNullableTextInputProps)

interface NullableTextInputProps {
    nullable: true
    value: string | null
    setValue(newValue: string | null): void
}

interface NonNullableTextInputProps {
    nullable?: false
    value: string
    setValue(newValue: string): void
}

const TextInput = ({ nullable, id, value, setValue }: TextInputProps) => {
    return (
        <input
            id={id}
            type="text"
            value={value ?? ''}
            onChange={e => {
                if (nullable) {
                    setValue(e.target.value === '' ? null : e.target.value)
                } else {
                    setValue(e.target.value)
                }
            }}
        />
    )
}

interface TimeInputProps {
    id: string
    value: number | null
    setValue(newValue: number | null): void
}

const TimeInput = ({ id, value, setValue }: TimeInputProps) => {
    return (
        <input
            id={id}
            value={value ?? ''}
            onChange={e => {
                if (e.target.value === '') {
                    setValue(null)
                } else {
                    const parsed = parseInt(e.target.value)
                    if (!Number.isNaN(parsed)) {
                        setValue(parsed)
                    }
                }
            }}
        />
    )
}

export interface EditRecipeProps {
    username: string
    slug: string
}

const EditRecipe = ({ username, slug }: EditRecipeProps) => {
    const { accessToken } = useAuth()
    const refresh = useRefresh()
    const queryClient = useQueryClient()
    const [spec, setSpec] = useState<UpdateRecipeSpec>({
        title: '',
        preparationTime: null,
        cookingTime: null,
        totalTime: null,
        rawText: '',
        mainImage: null,
        isPublic: false
    })

    const recipeQuery = useQuery(
        {
            queryKey: ['recipes', username, slug],
            queryFn: ({ signal }) =>
                getRecipe({
                    accessToken,
                    includeRawText: true,
                    refresh,
                    slug,
                    signal,
                    username
                })
        },
        queryClient
    )

    useEffect(() => {
        if (recipeQuery.isSuccess) {
            setSpec(fromFullRecipeView(recipeQuery.data.recipe))
        }
    }, [recipeQuery.isSuccess, recipeQuery.data])

    const mutation = useMutation(
        {
            mutationFn: () => {
                if (accessToken !== null) {
                    return updateRecipe({
                        spec,
                        accessToken,
                        refresh,
                        username,
                        slug
                    })
                } else {
                    return Promise.reject('Must be logged in.')
                }
            },
            onSuccess: data => {
                console.log(data)
                setSpec(fromFullRecipeView(data.recipe))
                queryClient.setQueryData(['recipes', username, slug], data)
            }
        },
        queryClient
    )

    const onSubmit: FormEventHandler<HTMLFormElement> = e => {
        e.preventDefault()
        mutation.mutate()
        return false
    }

    const getSetSpecText =
        (prop: keyof UpdateRecipeSpec) =>
        (value: string): void => {
            const next = { ...spec }
            ;(next as any)[prop] = value
            setSpec(next)
        }

    const getSetTimeSpec =
        (prop: keyof UpdateRecipeSpec) =>
        (value: number | null): void => {
            const next = { ...spec }
            ;(next as any)[prop] = value
            setSpec(next)
        }

    const getSetSpecTextAsHandler =
        (prop: keyof UpdateRecipeSpec): ChangeEventHandler<HTMLTextAreaElement | HTMLInputElement> =>
        (event): void => {
            const next = { ...spec }
            ;(next as any)[prop] = event.target.value
            setSpec(next)
        }

    if (recipeQuery.isPending) {
        console.log('we are pending')
        return 'Loading...'
    } else if (recipeQuery.isError) {
        console.log('we had an error')
        const { error } = recipeQuery
        if (error instanceof ApiError) {
            if (error.status === 404) {
                return 'No such recipe.'
            } else {
                return `ApiError: ${error.status} ${error.message}`
            }
        } else {
            return `Error: ${error.name} ${error.message}`
        }
    } else if (recipeQuery.isSuccess && !recipeQuery.data.isOwner) {
        console.log('we are not the owner')
        return 'You do not have permission to edit this recipe.'
    } else {
        console.log('doing whole page')
        return (
            <div className={classes.articleContainer}>
                <article>
                    <h1>Edit Recipe</h1>
                    <form className={classes.editForm} onSubmit={onSubmit}>
                        <Control id="title" displayName="Title">
                            <TextInput id="title" value={spec.title} setValue={getSetSpecText('title')} />
                        </Control>

                        <Control id="preparation-time" displayName="Preparation Time (in minutes)">
                            <TimeInput
                                id="preparation-time"
                                value={spec.preparationTime}
                                setValue={getSetTimeSpec('preparationTime')}
                            />
                        </Control>

                        <Control id="cooking-time" displayName="Cooking Time (in minutes)">
                            <TimeInput
                                id="cooking-time"
                                value={spec.cookingTime}
                                setValue={getSetTimeSpec('cookingTime')}
                            />
                        </Control>

                        <Control id="total-time" displayName="Total Time (in minutes)">
                            <TimeInput id="total-time" value={spec.totalTime} setValue={getSetTimeSpec('totalTime')} />
                        </Control>

                        <Control id="recipe-text" displayName="Recipe Text">
                            <textarea
                                id="recipe-text"
                                value={spec.rawText}
                                onChange={getSetSpecTextAsHandler('rawText')}
                            />
                        </Control>

                        <div className={classes.submitContainer}>
                            <input type="submit" />
                            {mutation.isPending
                                ? 'Saving...'
                                : mutation.isSuccess
                                  ? 'Saved!'
                                  : mutation.isError
                                    ? `Error! ${mutation.error}`
                                    : null}
                        </div>
                    </form>
                </article>
            </div>
        )
    }
}

export default EditRecipe
