import { compact, get, sortBy } from "lodash";
import React, { useContext, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { setCurrentStep } from "../booking/booking-slice";
import {
  selectFormRooms,
  selectTravelersFormValues,
  setFormValues,
} from "./travelers-form-slice";

import { Link, navigate } from "@reach/router";
import { format, parse } from "date-fns";
import flat from "flat";
import { useFormik } from "formik";
import produce from "immer";
import { buildClassName } from "../../../shared/utils/class-util";
import LabeledInput from "../../components/labeled-input";
import LabeledSelect from "../../components/labeled-select";
import SettingsContext from "../../settings-context";
import { useAppDispatch } from "../../store";
import { TravelersFormValues } from "../../types";
import { setBookingType } from "../booking/booking-slice";
import { OPTIONS_FORM_STEP, SUMMARY_STEP } from "../booking/constants";
import {
  selectAgentAdressId,
  selectAgents,
  selectBookingQueryString,
  selectBookingType,
  selectStartDate,
  selectTranslations,
} from "../booking/selectors";
import { fetchPriceDetails } from "../price-details/price-details-slice";
import TypeAheadInput from "./type-ahead-input";
import validateForm from "./validate-form";

interface TravelersFormProps {}

function createTraveler(id: number) {
  return { id, firstName: "", lastName: "", birthDate: "", gender: "" };
}

function createInitialValues(
  formRooms: { adults: number[]; children: number[] }[],
  startDate?: string,
  agentAdressId?: number
) {
  const initialValues = {
    startDate: startDate,
    rooms: formRooms.map((r) => ({
      adults: r.adults.map((id) => createTraveler(id)),
      children: r.children.map((id) => createTraveler(id)),
    })),
    mainBookerId: -1,
    street: "",
    houseNumber: "",
    box: "",
    zipCode: "",
    place: "",
    country: "",
    phone: "",
    email: "",
    emailConfirmation: "",
    travelAgentId: agentAdressId ?? 0,
    travelAgentName: "",
  };

  if (
    initialValues.rooms &&
    initialValues.rooms.length &&
    initialValues.rooms[0].adults &&
    initialValues.rooms[0].adults.length
  ) {
    initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
  }

  return initialValues;
}

const TravelersForm: React.FC<TravelersFormProps> = () => {
  const dispatch = useAppDispatch();

  const settings = useContext(SettingsContext);
  const bookingQueryString = useSelector(selectBookingQueryString);
  const startDate = useSelector(selectStartDate);
  const formRooms = useSelector(selectFormRooms);
  const bookingType = useSelector(selectBookingType);
  const agents = useSelector(selectAgents);
  const agentAdressId = useSelector(selectAgentAdressId);
  const translations = useSelector(selectTranslations);

  const initialValues =
    useSelector(selectTravelersFormValues) ??
    createInitialValues(formRooms, startDate, agentAdressId);

  const [showAgents, setShowAgents] = useState<boolean>(
    settings.agentRequired ?? false
  );
  const [showAgentSelection, setShowAgentSelection] = useState<boolean>(
    !settings.agentAdressId && !settings.hideAgentSelection
  );

  const typeaheadAgents =
    sortBy(
      agents?.map((x) => ({
        key: `${x.id}`,
        value: `${x.name} (${x.postalCode} ${x.location})`,
        text: `${x.name} (${x.postalCode} ${x.location})`,
      })),
      "value"
    ) ?? [];

  const [filteredAgents, setFilteredAgents] =
    useState<{ key: string; value: string; text: string }[]>(typeaheadAgents);

  const formik = useFormik<TravelersFormValues>({
    initialValues,
    validate: (values) =>
      validateForm(values, settings.agentRequired, bookingType, translations),
    onSubmit: (values) => {
      dispatch(setFormValues(values));
      dispatch(fetchPriceDetails());

      if (settings.skipRouter) {
        dispatch(setCurrentStep(SUMMARY_STEP));
      } else {
        navigate(
          `${settings.basePath}${settings.summary.pathSuffix}?${bookingQueryString}`
        );
      }
    },
  });

  useEffect(() => {
    dispatch(fetchPriceDetails());
  }, []);

  useEffect(() => {
    if (agents && settings.affiliateSlug) {
      const agent = agents.find(
        (x) => x.affiliateSlug && x.affiliateSlug === settings.affiliateSlug
      );
      if (!agent) return;

      const formValues = produce(formik.values, (values) => {
        (values.travelAgentId = Number(agent.id)),
          (values.travelAgentName = agent.name);
      });
      formik.setValues(formValues, false);
      dispatch(setFormValues(formValues));
      setShowAgentSelection(false);
    }
  }, [agents, settings.affiliateSlug]);

  const handleMainBookerChange: React.FormEventHandler<HTMLInputElement> = (
    e
  ) => {
    const id = parseInt(e.currentTarget.value);

    formik.setFieldValue("mainBookerId", id);
  };

  const mainBooker = formik.values.rooms
    .find((r) =>
      r.adults.find((traveler) => traveler.id === formik.values.mainBookerId)
    )
    ?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);

  const handleAgentChange = (value: string) => {
    const filteredAgents = typeaheadAgents.filter(
      (x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
    );

    setFilteredAgents(filteredAgents);
    formik.setFieldValue("travelAgentName", value);
  };

  const handleAgentSelect = (key: string) => {
    const agent = typeaheadAgents.find((x) => x.key === key);

    formik.setValues({
      ...formik.values,
      travelAgentId: Number(agent?.key),
      travelAgentName: agent?.value ?? "",
    });

    let bookingType = "b2b2c";
    if (agentAdressId && agentAdressId != 0) {
      bookingType = "b2b";
    }
    dispatch(setBookingType(bookingType));
  };

  const handleAgentClear = () => {
    formik.setValues({
      ...formik.values,
      travelAgentId: 0,
      travelAgentName: "",
    });

    dispatch(setBookingType("b2c"));
  };

  const toggleAgent = (value: boolean) => {
    setShowAgents(value);

    if (!value) {
      handleAgentClear();
      setFilteredAgents([]);
    }
  };

  const goPrevious = () => {
    dispatch(setCurrentStep(OPTIONS_FORM_STEP));
  };

  const flatErrors: Record<string, string> = flat(formik.errors);
  const hasVisibleError = (key: string) =>
    get(formik.errors, key) && get(formik.touched, key);

  return (
    <form
      className="form form__travelers"
      name="booking--travellers"
      id="booking--travellers"
      noValidate
      onSubmit={formik.handleSubmit}
      onReset={formik.handleReset}
    >
      <div className="form__travelers__wrapper">
        {formik.values.rooms.map((room, rIndex) => (
          <div key={rIndex}>
            <div className="form__region">
              <div className="form__region-header">
                <h5 className="form__region-heading">
                  {translations.SHARED.ROOM} {rIndex + 1}
                </h5>
                <p className="form__region-label">
                  {compact([
                    room.adults.length,
                    room.adults.length === 1 &&
                      ` ${translations.TRAVELERS_FORM.ADULT}`,
                    room.adults.length > 1 &&
                      ` ${translations.TRAVELERS_FORM.ADULTS}`,
                    room.adults &&
                      room.adults.length &&
                      room.children &&
                      room.children.length &&
                      ", ",
                    room.children.length,
                    room.children.length === 1 &&
                      ` ${translations.TRAVELERS_FORM.CHILD}`,
                    room.children.length > 1 &&
                      ` ${translations.TRAVELERS_FORM.CHILDREN}`,
                  ]).join("")}
                </p>
              </div>
            </div>
            {room.adults.map((travelerValues, index) => (
              <div className="form__region" key={travelerValues.id}>
                <div className="form__region-header">
                  <h5 className="form__region-heading">
                    {translations.TRAVELERS_FORM.TRAVELER} {index + 1}
                  </h5>
                  <p className="form__region-label">
                    {translations.TRAVELERS_FORM.ADULT}
                  </p>

                  <div className="radiobutton">
                    <label className="radiobutton__label">
                      <input
                        type="radio"
                        name="mainBookerId"
                        onChange={handleMainBookerChange}
                        onBlur={formik.handleBlur}
                        value={travelerValues.id}
                        checked={
                          formik.values.mainBookerId === travelerValues.id
                        }
                        className="radiobutton__input"
                      />
                      {translations.TRAVELERS_FORM.MAIN_BOOKER}
                    </label>
                  </div>
                </div>
                <div className="form__row">
                  <div
                    className={buildClassName([
                      "form__group",
                      hasVisibleError(
                        `rooms[${rIndex}].adults[${index}].gender`
                      ) && "form__group--error",
                    ])}
                  >
                    <label className="form__label">
                      {translations.TRAVELERS_FORM.GENDER} *
                    </label>
                    <div className="radiobutton-group">
                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].adults[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="m"
                            checked={travelerValues.gender === "m"}
                          />
                          {translations.TRAVELERS_FORM.MALE}
                        </label>
                      </div>

                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].adults[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="f"
                            checked={travelerValues.gender === "f"}
                          />
                          {translations.TRAVELERS_FORM.FEMALE}
                        </label>
                      </div>

                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].adults[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="x"
                            checked={travelerValues.gender === "x"}
                          />
                          {translations.TRAVELERS_FORM.OTHER}
                        </label>
                      </div>
                    </div>
                  </div>
                </div>
                <div className="form__row">
                  <LabeledInput
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].adults[${index}].firstName`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.FIRST_NAME}
                    required
                    name={`rooms[${rIndex}].adults[${index}].firstName`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.firstName}
                  />
                  <LabeledInput
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].adults[${index}].lastName`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.LAST_NAME}
                    required
                    name={`rooms[${rIndex}].adults[${index}].lastName`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.lastName}
                  />
                  <LabeledInput
                    type="date"
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].adults[${index}].birthDate`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.BIRTHDATE}
                    required
                    name={`rooms[${rIndex}].adults[${index}].birthDate`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.birthDate}
                  />
                </div>
              </div>
            ))}
            {room.children.map((travelerValues, index) => (
              <div className="form__region" key={travelerValues.id}>
                <div className="form__region-header">
                  <h5 className="form__region-heading">
                    {translations.TRAVELERS_FORM.TRAVELER}{" "}
                    {room.adults.length + index + 1}
                  </h5>
                  <p className="form__region-label">
                    {translations.TRAVELERS_FORM.CHILD}
                  </p>
                </div>
                <div className="form__row">
                  <div
                    className={buildClassName([
                      "form__group",
                      hasVisibleError(
                        `rooms[${rIndex}].children[${index}].gender`
                      ) && "form__group--error",
                    ])}
                  >
                    <label className="form__label">
                      {translations.TRAVELERS_FORM.GENDER} *
                    </label>
                    <div className="radiobutton-group">
                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].children[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="m"
                            checked={travelerValues.gender === "m"}
                          />
                          {translations.TRAVELERS_FORM.MALE}
                        </label>
                      </div>

                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].children[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="f"
                            checked={travelerValues.gender === "f"}
                          />
                          {translations.TRAVELERS_FORM.FEMALE}
                        </label>
                      </div>

                      <div className="radiobutton">
                        <label className="radiobutton__label">
                          <input
                            type="radio"
                            className="radiobutton__input"
                            name={`rooms[${rIndex}].children[${index}].gender`}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                            value="x"
                            checked={travelerValues.gender === "x"}
                          />
                          {translations.TRAVELERS_FORM.OTHER}
                        </label>
                      </div>
                    </div>
                  </div>
                </div>
                <div className="form__row">
                  <LabeledInput
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].children[${index}].firstName`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.FIRST_NAME}
                    required
                    name={`rooms[${rIndex}].children[${index}].firstName`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.firstName}
                  />
                  <LabeledInput
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].children[${index}].lastName`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.LAST_NAME}
                    required
                    name={`rooms[${rIndex}].children[${index}].lastName`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.lastName}
                  />
                  <LabeledInput
                    type="date"
                    hasError={hasVisibleError(
                      `rooms[${rIndex}].children[${index}].birthDate`
                    )}
                    extraClassName="form__group--md-33"
                    label={translations.TRAVELERS_FORM.BIRTHDATE}
                    required
                    name={`rooms[${rIndex}].children[${index}].birthDate`}
                    onChange={formik.handleChange}
                    onBlur={formik.handleBlur}
                    value={travelerValues.birthDate}
                  />
                </div>
              </div>
            ))}
          </div>
        ))}

        {bookingType != "b2b" ? (
          <div className="form__region">
            <div className="form__region-header">
              <h5 className="form__region-heading">
                {translations.TRAVELERS_FORM.MAIN_BOOKER}
              </h5>
              <p className="form__region-label">
                {compact([
                  compact([mainBooker?.firstName, mainBooker?.lastName]).join(
                    " "
                  ),
                  mainBooker?.birthDate &&
                    format(
                      parse(mainBooker.birthDate, "yyyy-MM-dd", new Date()),
                      "dd-MM-yyyy"
                    ),
                ]).join(", ")}
              </p>
            </div>
            <>
              <div className="form__twocolumn">
                <div className="form__twocolumn-column">
                  <div className="form__row">
                    <LabeledInput
                      hasError={hasVisibleError("street")}
                      extraClassName="form__group--50 form__group--sm-60"
                      label={translations.TRAVELERS_FORM.STREET}
                      required
                      name="street"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.street}
                    />
                    <LabeledInput
                      hasError={hasVisibleError("houseNumber")}
                      extraClassName="form__group--30 form__group--sm-20"
                      label={translations.TRAVELERS_FORM.HOUSE_NUMBER}
                      required
                      name="houseNumber"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.houseNumber}
                    />
                    <LabeledInput
                      hasError={hasVisibleError("box")}
                      extraClassName="form__group--20"
                      label={translations.TRAVELERS_FORM.POST_BOX}
                      name="box"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.box}
                    />
                  </div>
                </div>
                <div className="form__twocolumn-column">
                  <div className="form__row">
                    <LabeledInput
                      hasError={hasVisibleError("zipCode")}
                      extraClassName="form__group--40 form__group--sm-20"
                      label={translations.TRAVELERS_FORM.ZIPCODE}
                      required
                      name="zipCode"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.zipCode}
                    />
                    <LabeledInput
                      hasError={hasVisibleError("place")}
                      extraClassName="form__group--60 form__group--sm-40"
                      label={translations.TRAVELERS_FORM.CITY}
                      required
                      name="place"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.place}
                    />
                    <LabeledSelect
                      hasError={hasVisibleError("country")}
                      extraClassName="form__group--sm-40"
                      label={translations.TRAVELERS_FORM.COUNTRY}
                      required
                      name="country"
                      onChange={formik.handleChange}
                      onBlur={formik.handleBlur}
                      value={formik.values.country}
                      options={[
                        {
                          key: "empty",
                          label: translations.TRAVELERS_FORM.SELECT_COUNTRY,
                          value: undefined,
                        },
                        {
                          key: "be",
                          value: "be",
                          label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM,
                        },
                        {
                          key: "nl",
                          value: "nl",
                          label:
                            translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS,
                        },
                        {
                          key: "fr",
                          value: "fr",
                          label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE,
                        },
                      ]}
                    />
                  </div>
                </div>
              </div>
              <div className="form__row">
                <LabeledInput
                  hasError={hasVisibleError("phone")}
                  extraClassName="form__group--md-33"
                  label={translations.TRAVELERS_FORM.PHONE}
                  required
                  name="phone"
                  onChange={formik.handleChange}
                  onBlur={formik.handleBlur}
                  value={formik.values.phone}
                />
                <LabeledInput
                  type="email"
                  hasError={hasVisibleError("email")}
                  extraClassName="form__group--md-33"
                  label={translations.TRAVELERS_FORM.EMAIL}
                  required
                  name="email"
                  onChange={formik.handleChange}
                  onBlur={formik.handleBlur}
                  value={formik.values.email}
                />
                <LabeledInput
                  type="email"
                  hasError={hasVisibleError("emailConfirmation")}
                  extraClassName="form__group--md-33"
                  label={translations.TRAVELERS_FORM.REPEAT_EMAIL}
                  required
                  name="emailConfirmation"
                  onChange={formik.handleChange}
                  onBlur={formik.handleBlur}
                  value={formik.values.emailConfirmation}
                />
              </div>
            </>
          </div>
        ) : (
          <div className="form__region">
            <div className="form__row">
              <LabeledInput
                hasError={hasVisibleError("phone")}
                extraClassName="form__group--md-33"
                label={translations.TRAVELERS_FORM.PHONE}
                required
                name="phone"
                onChange={formik.handleChange}
                onBlur={formik.handleBlur}
                value={formik.values.phone}
              />
            </div>
          </div>
        )}

        {showAgentSelection && (
          <div className="form__region">
            <div className="form__region-header">
              <h5 className="form__region-heading">
                {translations.TRAVELERS_FORM.BOOK_WITH_AGENT}
              </h5>
              <div className="checkbox" id="cbxChooseOffice">
                <label className="checkbox__label">
                  <input
                    type="checkbox"
                    name="booking--mainbooker"
                    defaultChecked={showAgents}
                    onClick={() => toggleAgent(!showAgents)}
                    className="checkbox__input"
                  />
                  {translations.TRAVELERS_FORM.CHOOSE_OFFICE}
                </label>
              </div>
            </div>
            {showAgents && (
              <div className="form__row form__row--choose-office">
                <div
                  className={buildClassName([
                    "form__group",
                    "form__group--icon",
                    hasVisibleError("travelAgentId") && "form__group--error",
                  ])}
                >
                  <TypeAheadInput
                    value={formik.values.travelAgentName}
                    options={filteredAgents}
                    onChange={handleAgentChange}
                    onSelect={handleAgentSelect}
                    onClear={handleAgentClear}
                    name="travelAgentName"
                    placeholder={
                      translations.TRAVELERS_FORM.CHOOSE_AGENT_PLACEHOLDER
                    }
                  />
                </div>
              </div>
            )}
          </div>
        )}
      </div>
      {Object.keys(flatErrors).length > 0 && (
        <div className="form__region form__region--errors">
          <div className="form__row">
            <div className="form__group">
              <p className="form__error-heading">
                {translations.TRAVELERS_FORM.VALIDATION_MESSAGE}:
              </p>
              <ul className="list">
                {Object.keys(flatErrors).map((key) => (
                  <li key={key}>{get(flatErrors, key)}</li>
                ))}
              </ul>
            </div>
          </div>
        </div>
      )}
      <div className="booking__navigator">
        {settings.skipRouter ? (
          <button
            type="button"
            title={translations.STEPS.PREVIOUS}
            onClick={() => goPrevious()}
            className="cta cta--secondary"
          >
            {translations.STEPS.PREVIOUS}
          </button>
        ) : (
          <Link
            to={`${settings.basePath}${settings.options.pathSuffix}?${bookingQueryString}`}
            title={translations.STEPS.PREVIOUS}
            className="cta cta--secondary"
          >
            {translations.STEPS.PREVIOUS}
          </Link>
        )}
        <button type="submit" title={translations.STEPS.NEXT} className="cta">
          {translations.STEPS.NEXT}
        </button>
      </div>
    </form>
  );
};

export default TravelersForm;
