import React, { useCallback } from "react";
import { Edition } from "@/data/edition";
import { Nullable } from "@/helpers";
import { useTranslation } from "react-i18next";
import { FieldProps, Field, FieldArrayRenderProps, FieldArray, getIn } from "formik";
import { identityTransformer, Transformer } from "@/serialization";
import {
    Button, Card, CardContent, CardHeader,
    Checkbox,
    Grid, IconButton,
    List,
    ListItem,
    ListItemIcon,
    ListItemSecondaryAction,
    ListItemText,
    Paper,
    TextField,
    Tooltip,
    Typography
} from "@material-ui/core";
import { useSpacing } from "@/styles";
import { Moment } from "moment-timezone";
import { KeyboardDatePicker as DatePicker } from "@material-ui/pickers";
import { TextField as TextFieldFormik } from "formik-material-ui";
import { Course, Identifiable, InternshipProgramEntry, InternshipType } from "@/data";
import { Autocomplete } from "@material-ui/lab";
import { useAsync } from "@/hooks";
import api from "@/management/api";
import { Async } from "@/components/async";
import { AccountCheck, ArrowDown, ArrowUp, ShieldCheck, TrashCan } from "mdi-material-ui";
import { Actions } from "@/components";
import { Add } from "@material-ui/icons";

export type EditionFormValues = Nullable<Edition>;

export const initialEditionFormValues: EditionFormValues = {
    course: null,
    endDate: null,
    minimumInternshipHours: 80,
    proposalDeadline: null,
    reportingEnd: null,
    reportingStart: null,
    startDate: null,
    types: [],
    program: [],
}

export const editionFormValuesTransformer: Transformer<Edition, EditionFormValues> = identityTransformer;

function toggleValueInArray<T extends Identifiable>(array: T[], value: T, comparator: (a: T, b: T) => boolean = (a, b) => a == b): T[] {
    return array.findIndex(other => comparator(other, value)) === -1
        ? [ ...array, value ]
        : array.filter(other => !comparator(other, value));
}

export const ProgramField = ({ remove, swap, push, form, name, ...props }: FieldArrayRenderProps) => {
    const value = getIn(form.values, name) as InternshipProgramEntry[];

    const { t } = useTranslation("management");

    return <>
        { value.map((entry, index) => <Card>
            <CardHeader
                subheader={ t('edition.program.entry', { index: index + 1 }) }
                action={ <>
                    { index < value.length - 1 && <IconButton onClick={ () => swap(index, index + 1) }><ArrowDown /></IconButton> }
                    { index > 0 && <IconButton onClick={ () => swap(index, index - 1) }><ArrowUp /></IconButton> }
                    <IconButton onClick={ () => remove(index) }><TrashCan /></IconButton>
                </> }
            />
            <CardContent>
                <Field component={ TextFieldFormik }
                       label={ t('edition.program.field.description') }
                       name={`${name}[${index}].description`}
                       fullWidth
                />
            </CardContent>
        </Card>) }
        <Actions>
            <Button variant="contained" color="primary" startIcon={ <Add /> } onClick={ () => push({ description: "" }) }>{ t("actions.add") }</Button>
        </Actions>
    </>
}

export const TypesField = ({ field, form, meta, ...props }: FieldProps<InternshipType[]>) => {
    const { name, value = [] } = field;

    const types = useAsync(useCallback(() => api.type.all(), []));
    const { t } = useTranslation("management");

    const toggle = (type: InternshipType) => () => form.setFieldValue(name, toggleValueInArray(value, type, (a, b) => a.id == b.id));
    const isChecked = (type: InternshipType) => value.findIndex(v => v.id == type.id) !== -1;

    return <Async async={ types }>
        { types => <List>{
            types.map(type => <ListItem dense button onClick={ toggle(type) }>
                <ListItemIcon>
                    <Checkbox edge="start" onChange={ toggle(type) } checked={ isChecked(type) }/>
                </ListItemIcon>
                <ListItemText>
                    <div>{ type.label.pl }</div>
                    <Typography variant="caption">{ type.description?.pl }</Typography>
                </ListItemText>
                <ListItemSecondaryAction>
                    <div style={{ display: "flex", flexDirection: "column" }}>
                        { type.requiresDeanApproval && <Tooltip title={ t("type.flag.dean-approval") as string }><AccountCheck/></Tooltip> }
                        { type.requiresInsurance && <Tooltip title={ t("type.flag.insurance") as string }><ShieldCheck/></Tooltip> }
                    </div>
                </ListItemSecondaryAction>
            </ListItem>)
        }</List> }
    </Async>
}

export const CoursePickerField = ({ field, form, meta, ...props }: FieldProps<Course>) => {
    const courses = useAsync(useCallback(() => api.course.all(), []));
    const { t } = useTranslation("management");

    return <Autocomplete
        options={ courses.isLoading ? [] : courses.value as Course[] }
        renderInput={ props => <TextField { ...props } label={ t("edition.field.course") } fullWidth/> }
        getOptionLabel={ course => course.name }
        value={ field.value }
        onChange={ field.onChange }
        onBlur={ field.onBlur }
    />
}

export const DatePickerField = ({ field, form, meta, ...props }: FieldProps<Moment>) => {
    const { value, onChange, onBlur } = field;

    return <DatePicker value={ value }
                       onChange={ onChange }
                       onBlur={ onBlur }
                       { ...props }
                       format="DD.MM.yyyy"
                       disableToolbar fullWidth
                       variant="inline"
    />
}

export const EditionForm = () => {
    const { t } = useTranslation("management");
    const spacing = useSpacing(2);

    return <div className={ spacing.vertical }>
        <Typography variant="h5">{ t("edition.fields.basic") }</Typography>
        <Grid container>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="startDate" component={ DatePickerField } label={ t("edition.field.start") } />
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="endDate" component={ DatePickerField } label={ t("edition.field.end") } />
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="course" component={ CoursePickerField } label={ t("edition.field.course") } />
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="minimumInternshipHours" component={ TextFieldFormik } label={ t("edition.field.minimumInternshipHours") } />
            </Grid>
        </Grid>
        <Typography variant="h5">{ t("edition.fields.deadlines") }</Typography>
        <Grid container>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="proposalDeadline" component={ DatePickerField } label={ t("edition.field.proposalDeadline") } />
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="reportingStart" component={ DatePickerField } label={ t("edition.field.reportingStart") } />
            </Grid>
            <Grid item xs={ 12 } md={ 6 }>
                <Field name="reportingEnd" component={ DatePickerField } label={ t("edition.field.reportingEnd") } />
            </Grid>
        </Grid>
        <Typography variant="h5">{ t("edition.fields.program") }</Typography>
        <FieldArray name="program" component={ ProgramField as any } />
        <Typography variant="h5">{ t("edition.fields.types") }</Typography>
        <Paper elevation={ 2 }>
            <Field name="types" component={ TypesField } />
        </Paper>
    </div>
}