import React, { HTMLProps, useMemo, useState } from "react";
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, Grid, TextField, Typography } from "@material-ui/core";
import { KeyboardDatePicker as DatePicker } from "@material-ui/pickers";
import { CompanyForm } from "@/forms/company";
import { StudentForm } from "@/forms/student";
import { sampleStudent } from "@/provider/dummy/student";
import { Company, Internship, InternshipType, internshipTypeLabels, Office, Student } from "@/data";
import { Nullable } from "@/helpers";
import moment, { Moment } from "moment";
import { computeWorkingHours } from "@/utils/date";
import { Autocomplete } from "@material-ui/lab";
import { emptyInternship } from "@/provider/dummy/internship";
import { InternshipProposalActions, useDispatch } from "@/state/actions";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { AppState } from "@/state/reducer";
import { Link as RouterLink, useHistory } from "react-router-dom";
import { route } from "@/routing";
import { getInternshipProposal } from "@/state/reducer/proposal";
import { Actions } from "@/components";
import { Field, Form, Formik, useFormikContext } from "formik";
import * as Yup from "yup";
import { Transformer } from "@/serialization";
import { TextField as TextFieldFormik } from "formik-material-ui"
import { Edition } from "@/data/edition";
import { useUpdateEffect } from "@/hooks";

export type InternshipFormValues = {
    startDate: Moment | null;
    endDate: Moment | null;
    hours: number | "";
    workingHours: number;
    companyName: string;
    companyNip: string;
    city: string;
    postalCode: string;
    country: string;
    street: string;
    building: string;
    mentorFirstName: string;
    mentorLastName: string;
    mentorEmail: string;
    mentorPhone: string;
    kindOther: string | null;

    // relations
    kind: InternshipType | null;
    company: Company | null;
    office: Office | null;
    student: Student;
}

const emptyInternshipValues: InternshipFormValues = {
    building: "",
    city: "",
    company: null,
    companyName: "",
    companyNip: "",
    country: "",
    street: "",
    endDate: null,
    hours: "",
    kind: null,
    kindOther: "",
    mentorEmail: "",
    mentorFirstName: "",
    mentorLastName: "",
    mentorPhone: "",
    office: null,
    postalCode: "",
    startDate: null,
    student: sampleStudent,
    workingHours: 40,
}

export const InternshipTypeItem = ({ type, ...props }: { type: InternshipType } & HTMLProps<any>) => {
    const info = internshipTypeLabels[type];

    return (
        <div className="internship=type-item" { ...props }>
            <div>{ info.label }</div>
            { info.description && <Typography variant="caption">{ info.description }</Typography> }
        </div>
    )
}

const InternshipProgramForm = () => {
    const { t } = useTranslation();
    const { values, handleBlur, setFieldValue, errors } = useFormikContext<InternshipFormValues>();

    return (
        <Grid container>
            <Grid item md={ 4 }>
                <Autocomplete renderInput={ props => <TextField { ...props } label={ t("forms.internship.fields.kind") } fullWidth error={ !!errors.kind } helperText={ errors.kind }/> }
                              getOptionLabel={ (option: InternshipType) => internshipTypeLabels[option].label }
                              renderOption={ (option: InternshipType) => <InternshipTypeItem type={ option }/> }
                              options={ Object.values(InternshipType) as InternshipType[] }
                              disableClearable
                              value={ values.kind || undefined }
                              onChange={ (_, value) => setFieldValue("kind", value) }
                              onBlur={ handleBlur }
                />
            </Grid>
            <Grid item md={ 8 }>
                {
                    values.kind === InternshipType.Other &&
                    <Field label={ t("forms.internship.fields.kind-other") } name="kindOther" fullWidth component={ TextFieldFormik } />
                }
            </Grid>
        </Grid>
    )
}

const InternshipDurationForm = () => {
    const { t } = useTranslation();
    const {
        values: { startDate, endDate, workingHours },
        errors,
        touched,
        setFieldTouched,
        setFieldValue
    } = useFormikContext<InternshipFormValues>();

    const [overrideHours, setHoursOverride] = useState<number | null>(null)

    const computedHours = useMemo(() => startDate && endDate && computeWorkingHours(startDate, endDate, workingHours / 5), [startDate, endDate, workingHours]);

    const hours = useMemo(() => overrideHours !== null ? overrideHours : computedHours || null, [overrideHours, computedHours]);
    const weeks = useMemo(() => hours !== null ? Math.floor(hours / workingHours) : null, [ hours ]);

    useUpdateEffect(() => {
        setFieldTouched("hours", true);
        setFieldValue("hours", hours, true);
    }, [ hours ]);

    return (
        <Grid container>
            <Grid item md={ 6 }>
                <DatePicker value={ startDate } onChange={ value => setFieldValue("startDate", value) }
                            format="DD MMMM yyyy"
                            disableToolbar fullWidth
                            variant="inline" label={ t("forms.internship.fields.start-date") }
                            minDate={ moment() }
                />
            </Grid>
            <Grid item md={ 6 }>
                <DatePicker value={ endDate } onChange={ value => setFieldValue("endDate", value) }
                            format="DD MMMM yyyy"
                            disableToolbar fullWidth
                            variant="inline" label={ t("forms.internship.fields.end-date") }
                            minDate={ startDate || moment() }
                />
            </Grid>
            <Grid item md={ 4 }>
                <Field component={ TextFieldFormik }
                       name="workingHours"
                       label={ t("forms.internship.fields.working-hours") }
                       helperText={ t("forms.internship.help.working-hours") }
                       fullWidth
                />
            </Grid>
            <Grid item md={ 4 }>
                <TextField fullWidth
                           label={ t("forms.internship.fields.total-hours") }
                           error={ !!errors.hours && touched.hours }
                           helperText={ touched.hours && errors.hours }
                           value={ hours || "" }
                           onChange={ ev => setHoursOverride(parseInt(ev.target.value) || 0) }
                />
            </Grid>
            <Grid item md={ 4 }>
                <TextField fullWidth label={ t("forms.internship.fields.weeks") }
                           value={ weeks || "" }
                           disabled
                           helperText={ t("forms.internship.help.weeks") }
                />
            </Grid>
        </Grid>
    );
}

type InternshipConverterContext = {
    internship: Internship,
}

const converter: Transformer<Nullable<Internship>, InternshipFormValues, InternshipConverterContext> = {
    transform(internship: Nullable<Internship>): InternshipFormValues {
        return {
            student: internship.intern as Student,
            kind: internship.type,
            kindOther: "",
            startDate: internship.startDate,
            endDate: internship.endDate,
            hours: internship.hours || "",
            building: internship.office?.address?.building || "",
            office: internship.office,
            city: internship.office?.address?.city || "",
            postalCode: internship.office?.address?.postalCode || "",
            street: internship.office?.address?.street || "",
            country: internship.office?.address?.country || "",
            company: internship.company,
            companyName: internship.company?.name || "",
            companyNip: internship.company?.nip || "",
            mentorEmail: internship.mentor?.email || "",
            mentorFirstName: internship.mentor?.name || "",
            mentorLastName: internship.mentor?.surname || "",
            mentorPhone: internship.mentor?.phone || "",
            workingHours: 40,
        }
    },
    reverseTransform(form: InternshipFormValues, context: InternshipConverterContext): Nullable<Internship> {
        return {
            ...context.internship,
            startDate: form.startDate as Moment,
            endDate: form.endDate as Moment,
            office: form.office || {
                address: {
                    street: form.street,
                    postalCode: form.postalCode,
                    country: form.country,
                    city: form.city,
                    building: form.building,
                }
            },
            mentor: {
                surname: form.mentorLastName,
                name: form.mentorFirstName,
                email: form.mentorEmail,
                phone: form.mentorPhone,
            },
            company: form.company || {
                name: form.companyName,
                nip: form.companyNip,
                offices: [],
            },
            hours: form.hours as number,
            type: form.kind as InternshipType,
        }
    }
}

export const InternshipForm: React.FunctionComponent = () => {
    const initialInternship = useSelector<AppState, Nullable<Internship>>(state => getInternshipProposal(state.proposal) || {
        ...emptyInternship,
        office: null,
        company: null,
        mentor: null,
        intern: sampleStudent
    });

    const edition = useSelector<AppState, Edition>(state => state.edition as Edition);

    const { t } = useTranslation();


    const dispatch = useDispatch();
    const history = useHistory();

    const [confirmDialogOpen, setConfirmDialogOpen] = useState<boolean>(false);

    const validationSchema = Yup.object<Partial<InternshipFormValues>>({
        mentorFirstName: Yup.string().required(t("validation.required")),
        mentorLastName: Yup.string().required(t("validation.required")),
        mentorEmail: Yup.string()
            .required(t("validation.required"))
            .email(t("validation.email")),
        mentorPhone: Yup.string()
            .required(t("validation.required"))
            .matches(/^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$/, t("validation.phone")),
        hours: Yup.number()
            .min(edition.minimumInternshipHours, t("validation.internship.minimum-hours", { hours: edition.minimumInternshipHours })),
        companyName: Yup.string().when("company", {
            is: null,
            then: Yup.string().required(t("validation.required"))
        }),
        companyNip: Yup.string().when("company", {
            is: null,
            then: Yup.string()
                .required(t("validation.required"))
        }),
        street: Yup.string().required(t("validation.required")),
        country: Yup.string().required(t("validation.required")),
        city: Yup.string().required(t("validation.required")),
        postalCode: Yup.string().required(t("validation.required")),
        building: Yup.string().required(t("validation.required")),
        kindOther: Yup.string().when("kind", {
            is: (values: InternshipFormValues) => values?.kind === InternshipType.Other,
            then: Yup.string().required(t("validation.required"))
        })
    })

    const values = converter.transform(initialInternship);

    const handleSubmit = (values: InternshipFormValues) => {
        setConfirmDialogOpen(false);

        dispatch({
            type: InternshipProposalActions.Send,
            internship: converter.reverseTransform(values, {
                internship: initialInternship as Internship,
            }) as Internship
        });

        history.push(route("home"))
    }

    const InnerForm = () => {
        const { submitForm, validateForm } = useFormikContext();

        const handleSubmitConfirmation = async () => {
            const errors = await validateForm();

            if (Object.keys(errors).length == 0) {
                setConfirmDialogOpen(true);
            } else {
                console.warn(errors);
            }
        }

        const handleCancel = () => {
            setConfirmDialogOpen(false);
        }

        return <Form>
            <Typography variant="h3" className="section-header">{ t('internship.sections.intern-info') }</Typography>
            <StudentForm />
            <Typography variant="h3" className="section-header">{ t('internship.sections.kind' )}</Typography>
            <InternshipProgramForm />
            <Typography variant="h3" className="section-header">{ t('internship.sections.duration') }</Typography>
            <InternshipDurationForm />
            <Typography variant="h3" className="section-header">{ t('internship.sections.place') }</Typography>
            <CompanyForm />
            <Actions>
                <Button variant="contained" color="primary" onClick={ handleSubmitConfirmation }>{ t("confirm") }</Button>

                <Button component={ RouterLink } to={ route("home") }>
                    { t('go-back') }
                </Button>
            </Actions>

            <Dialog open={ confirmDialogOpen } onClose={ handleCancel }>
                <DialogContent>
                    <DialogContentText>{ t('forms.internship.send-confirmation') }</DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={ handleCancel }>{ t('cancel') }</Button>
                    <Button color="primary" autoFocus onClick={ submitForm }>{ t('confirm') }</Button>
                </DialogActions>
            </Dialog>
        </Form>
    }

    return (
        <Formik initialValues={ values }
                onSubmit={ handleSubmit }
                validationSchema={ validationSchema }
                validateOnChange={ false }
                validateOnBlur={ true }
        >
            <InnerForm />
        </Formik>
    )
}

