import { useNavigate } from '@tanstack/react-router';
import React, { useCallback, useEffect, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';

import { resetCompose } from 'pl-fe/actions/compose';
import {
  cancelEventCompose,
  initEventEdit,
  submitEvent,
} from 'pl-fe/actions/events';
import { uploadFile } from 'pl-fe/actions/media';
import { fetchStatus } from 'pl-fe/actions/statuses';
import { ADDRESS_ICONS } from 'pl-fe/components/autosuggest-location';
import LocationSearch from 'pl-fe/components/location-search';
import Button from 'pl-fe/components/ui/button';
import Form from 'pl-fe/components/ui/form';
import FormActions from 'pl-fe/components/ui/form-actions';
import FormGroup from 'pl-fe/components/ui/form-group';
import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon';
import IconButton from 'pl-fe/components/ui/icon-button';
import Input from 'pl-fe/components/ui/input';
import Stack from 'pl-fe/components/ui/stack';
import Text from 'pl-fe/components/ui/text';
import Toggle from 'pl-fe/components/ui/toggle';
import ContentTypeButton from 'pl-fe/features/compose/components/content-type-button';
import { isCurrentOrFutureDate } from 'pl-fe/features/compose/components/schedule-form';
import { ComposeEditor, DatePicker } from 'pl-fe/features/ui/util/async-components';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
import { makeGetStatus } from 'pl-fe/selectors';
import toast from 'pl-fe/toast';

import UploadButton from '../components/upload-button';

import type { Location } from 'pl-api';

const messages = defineMessages({
  eventNamePlaceholder: { id: 'compose_event.fields.name_placeholder', defaultMessage: 'Name' },
  eventDescriptionPlaceholder: { id: 'compose_event.fields.description_placeholder', defaultMessage: 'Description' },
  eventStartTimePlaceholder: { id: 'compose_event.fields.start_time_placeholder', defaultMessage: 'Event begins on…' },
  eventEndTimePlaceholder: { id: 'compose_event.fields.end_time_placeholder', defaultMessage: 'Event ends on…' },
  resetLocation: { id: 'compose_event.reset_location', defaultMessage: 'Reset location' },
  eventFetchFail: { id: 'compose_event.fetch_fail', defaultMessage: 'Failed to fetch edited event information' },
});

interface IEditEvent {
  statusId: string | null;
}

const EditEvent: React.FC<IEditEvent> = ({ statusId }) => {
  const intl = useIntl();
  const dispatch = useAppDispatch();
  const navigate = useNavigate();

  const getStatus = useCallback(makeGetStatus(), []);
  const status = useAppSelector((state) => statusId ? getStatus(state, { id: statusId }) : undefined);

  const [name, setName] = useState(status?.event?.name || '');
  const [text, setText] = useState('');
  const [startTime, setStartTime] = useState(status?.event?.start_time ? new Date(status.event.start_time) : new Date());
  const [endTime, setEndTime] = useState(status?.event?.end_time ? new Date(status.event.end_time) : null);
  const [approvalRequired, setApprovalRequired] = useState(status?.event?.join_mode !== 'free');
  const [banner, setBanner] = useState(status?.event?.banner || null);
  const [location, setLocation] = useState<Location | null>(null);

  const [isDisabled, setIsDisabled] = useState(!!statusId);
  const [isUploading, setIsUploading] = useState(false);

  const composeId = statusId ? `compose-event-modal-${statusId}` : 'compose-event-modal';

  const onChangeName: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
    setName(target.value);
  };

  const onChangeStartTime = (date: Date | null) => {
    setStartTime(date === null ? new Date : date);
  };

  const onChangeEndTime = (date: Date | null) => {
    setEndTime(date);
  };

  const onChangeHasEndTime: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
    if (target.checked) {
      const endTime = new Date(startTime);
      endTime.setHours(endTime.getHours() + 2);

      setEndTime(endTime);
    } else setEndTime(null);
  };

  const onChangeApprovalRequired: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
    setApprovalRequired(target.checked);
  };

  const onChangeLocation = (location: Location | null) => {
    setLocation(location);
  };

  const handleFiles = (files: FileList) => {
    setIsUploading(true);

    dispatch(uploadFile(
      files[0],
      intl,
      (data) => {
        setBanner(data);
        setIsUploading(false);
      },
      () => setIsUploading(false),
    ));
  };

  const handleClearBanner = () => {
    setBanner(null);
  };

  const handleSubmit = () => {
    setIsDisabled(true);

    dispatch(submitEvent({
      statusId,
      name,
      status: text,
      banner,
      startTime,
      endTime,
      joinMode: approvalRequired ? 'restricted' : 'free',
      location,
    })).then((status) => {
      if (status) navigate({ to: '/@{$username}/events/$statusId', params: { username: status.account.acct, statusId: status.id } });
      dispatch(resetCompose(composeId));
    }).catch(() => {
    });
  };

  useEffect(() => {
    if (statusId) {
      Promise.all([dispatch(initEventEdit(statusId)), dispatch(fetchStatus(statusId))])
        .then(([source, status]) => {
          if (!source || !status) throw new Error();

          setText(source.text);
          setLocation(source.location);

          setName(status?.event?.name || '');
          setStartTime(status?.event?.start_time ? new Date(status.event.start_time) : new Date());
          setEndTime(status?.event?.end_time ? new Date(status.event.end_time) : null);
          setApprovalRequired(status?.event?.join_mode !== 'free');
          setBanner(status?.media_attachments[0] || null);

          setIsDisabled(false);
        }).catch(() => {
          toast.error(messages.eventFetchFail);
        });
    }

    return () => {
      dispatch(cancelEventCompose());
    };
  }, [statusId]);

  const renderLocation = () => location && (
    <HStack className='h-[38px] text-gray-700 dark:text-gray-500' alignItems='center' space={2}>
      <Icon src={ADDRESS_ICONS[location.type] || require('@phosphor-icons/core/regular/map-pin.svg')} />
      <Stack className='grow'>
        <Text>{location.description}</Text>
        <Text theme='muted' size='xs'>{[location.street, location.locality, location.country].filter(val => val?.trim()).join(' · ')}</Text>
      </Stack>
      <IconButton title={intl.formatMessage(messages.resetLocation)} src={require('@phosphor-icons/core/regular/x.svg')} onClick={() => onChangeLocation(null)} />
    </HStack>
  );

  return (
    <Form className='⁂-edit-event' onSubmit={handleSubmit}>
      <FormGroup
        labelText={<FormattedMessage id='compose_event.fields.banner_label' defaultMessage='Event banner' />}
        hintText={<FormattedMessage id='compose_event.fields.banner_hint' defaultMessage='PNG, GIF or JPG. Landscape format is preferred.' />}
      >
        <div className='dark:sm:shadow-inset relative flex h-24 items-center justify-center overflow-hidden rounded-lg bg-primary-100 text-primary-500 dark:bg-gray-800 dark:text-white sm:h-32 sm:shadow'>
          {banner ? (
            <>
              <img className='size-full object-cover' src={banner.url} alt='' />
              <IconButton className='absolute right-2 top-2' src={require('@phosphor-icons/core/regular/x.svg')} onClick={handleClearBanner} />
            </>
          ) : (
            <UploadButton disabled={isUploading} onSelectFile={handleFiles} />
          )}
        </div>
      </FormGroup>
      <FormGroup
        labelText={<FormattedMessage id='compose_event.fields.name_label' defaultMessage='Event name' />}
      >
        <Input
          type='text'
          placeholder={intl.formatMessage(messages.eventNamePlaceholder)}
          value={name}
          onChange={onChangeName}
        />
      </FormGroup>
      <FormGroup
        labelText={<FormattedMessage id='compose_event.fields.description_label' defaultMessage='Event description' />}
      >
        <div className='relative'>
          <ContentTypeButton composeId={composeId} />
          <ComposeEditor
            key={String(isDisabled)}
            className='block w-full rounded-md border border-gray-400 bg-white px-3 py-2 text-base text-gray-900 ring-1 placeholder:text-gray-600 focus-within:border-primary-500 focus-within:ring-primary-500 black:bg-black dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500 sm:text-sm'
            placeholderClassName='pt-2'
            composeId={composeId}
            placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
            handleSubmit={handleSubmit}
            onChange={setText}
          />
        </div>
      </FormGroup>
      <FormGroup
        labelText={<FormattedMessage id='compose_event.fields.location_label' defaultMessage='Event location' />}
      >
        {location ? renderLocation() : (
          <LocationSearch
            onSelected={onChangeLocation}
          />
        )}
      </FormGroup>
      <FormGroup
        labelText={<FormattedMessage id='compose_event.fields.start_time_label' defaultMessage='Event start date' />}
      >
        <DatePicker
          showTimeSelect
          dateFormat='MMMM d, yyyy h:mm aa'
          timeIntervals={15}
          wrapperClassName='react-datepicker-wrapper'
          placeholderText={intl.formatMessage(messages.eventStartTimePlaceholder)}
          filterDate={isCurrentOrFutureDate}
          selected={startTime}
          onChange={onChangeStartTime}
        />
      </FormGroup>
      <HStack alignItems='center' space={2}>
        <Toggle
          checked={!!endTime}
          onChange={onChangeHasEndTime}
        />
        <Text tag='span' theme='muted'>
          <FormattedMessage id='compose_event.fields.has_end_time' defaultMessage='The event has an end date' />
        </Text>
      </HStack>
      {endTime && (
        <FormGroup
          labelText={<FormattedMessage id='compose_event.fields.end_time_label' defaultMessage='Event end date' />}
        >
          <DatePicker
            showTimeSelect
            dateFormat='MMMM d, yyyy h:mm aa'
            timeIntervals={15}
            wrapperClassName='react-datepicker-wrapper'
            placeholderText={intl.formatMessage(messages.eventEndTimePlaceholder)}
            filterDate={isCurrentOrFutureDate}
            selected={endTime}
            onChange={onChangeEndTime}
          />
        </FormGroup>
      )}
      {!statusId && (
        <HStack alignItems='center' space={2}>
          <Toggle
            checked={approvalRequired}
            onChange={onChangeApprovalRequired}
          />
          <Text tag='span' theme='muted'>
            <FormattedMessage id='compose_event.fields.approval_required' defaultMessage='I want to approve participation requests manually' />
          </Text>
        </HStack>
      )}
      <FormActions>
        <Button disabled={isDisabled} theme='primary' type='submit'>
          {statusId
            ? <FormattedMessage id='compose_event.update' defaultMessage='Update' />
            : <FormattedMessage id='compose_event.create' defaultMessage='Create' />}
        </Button>
      </FormActions>
    </Form>
  );
};

export { EditEvent };
