import React, { FormEventHandler, MouseEventHandler, useCallback, useEffect, useState } from 'react';
import { MatrixError, Room } from 'matrix-js-sdk';
import FocusTrap from 'focus-trap-react';
import { Box, Button, Chip, color, config, Input, Menu, MenuItem, PopOut, RectCords, Spinner, Switch, Text, TextArea } from 'folds';
import { Icon, Icons } from '../../components/icons';
import { SettingTile } from '../../components/setting-tile';
import { SequenceCard } from '../../components/sequence-card';
import {
  creatorsSupported,
  knockRestrictedSupported,
  knockSupported,
  restrictedSupported,
} from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { millisecondsToMinutes, replaceSpaceWithDash } from '../../utils/common';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { useCapabilities } from '../../hooks/useCapabilities';
import { useAlive } from '../../hooks/useAlive';
import { ErrorCode } from '../../cs-errorcode';
import {
  AdditionalCreatorInput,
  createRoom,
  CreateRoomAliasInput,
  CreateRoomData,
  CreateRoomKind,
  CreateRoomKindSelector,
  RoomVersionSelector,
  useAdditionalCreators,
} from '../../components/create-room';
import { RoomType, StateEvent } from '../../../types/matrix/room';
import { stopPropagation } from '../../utils/keyboard';
import { shouldShowForumLobby } from '../../utils/room';

const getCreateSpaceKindToIcon = (kind: CreateRoomKind) => {
  if (kind === CreateRoomKind.Private) return Icons.SpaceLock;
  if (kind === CreateRoomKind.Restricted) return Icons.Space;
  return Icons.SpaceGlobe;
};

const spaceTypeName: Record<RoomType.Space | RoomType.Forum, string> = {
  [RoomType.Space]: 'Space',
  [RoomType.Forum]: 'Forum Space',
};

type CreateSpaceFormProps = {
  defaultKind?: CreateRoomKind;
  space?: Room;
  onCreate?: (roomId: string) => void;
};
export function CreateSpaceForm({ defaultKind, space, onCreate }: CreateSpaceFormProps) {
  const mx = useMatrixClient();
  const alive = useAlive();

  const capabilities = useCapabilities();
  const roomVersions = capabilities['m.room_versions'];
  const [selectedRoomVersion, selectRoomVersion] = useState(roomVersions?.default ?? '1');
  useEffect(() => {
    // capabilities load async
    selectRoomVersion(roomVersions?.default ?? '1');
  }, [roomVersions?.default]);

  const allowRestricted = space && restrictedSupported(selectedRoomVersion);

  const [kind, setKind] = useState(
    defaultKind ?? allowRestricted ? CreateRoomKind.Restricted : CreateRoomKind.Private
  );

  const allowAdditionalCreators = creatorsSupported(selectedRoomVersion);
  const { additionalCreators, addAdditionalCreator, removeAdditionalCreator } =
    useAdditionalCreators();
  const [federation, setFederation] = useState(true);
  const [knock, setKnock] = useState(false);
  const [spaceType, setSpaceType] = useState<RoomType.Space | RoomType.Forum>(RoomType.Space);
  const [typeMenuAnchor, setTypeMenuAnchor] = useState<RectCords>();
  const [advance, setAdvance] = useState(false);

  const parentIsForum = Boolean(space && shouldShowForumLobby(space));

  const allowKnock = kind === CreateRoomKind.Private && knockSupported(selectedRoomVersion);
  const allowKnockRestricted =
    kind === CreateRoomKind.Restricted && knockRestrictedSupported(selectedRoomVersion);

  useEffect(() => {
    if (parentIsForum && spaceType === RoomType.Forum) {
      setSpaceType(RoomType.Space);
    }
  }, [parentIsForum, spaceType]);

  const handleRoomVersionChange = (version: string) => {
    if (!restrictedSupported(version)) {
      setKind(CreateRoomKind.Private);
    }
    selectRoomVersion(version);
  };

  const [createState, create] = useAsyncCallback<string, Error | MatrixError, [CreateRoomData]>(
    useCallback((data) => createRoom(mx, data), [mx])
  );
  const loading = createState.status === AsyncStatus.Loading;
  const error = createState.status === AsyncStatus.Error ? createState.error : undefined;
  const disabled = createState.status === AsyncStatus.Loading;

  const handleOpenTypeMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setTypeMenuAnchor(evt.currentTarget.getBoundingClientRect());
  };

  const handleTypeSelect = (type: RoomType.Space | RoomType.Forum) => {
    setSpaceType(type);
    setTypeMenuAnchor(undefined);
  };

  const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
    evt.preventDefault();
    if (disabled) return;
    const form = evt.currentTarget;

    const nameInput = form.nameInput as HTMLInputElement | undefined;
    const topicTextArea = form.topicTextAria as HTMLTextAreaElement | undefined;
    const aliasInput = form.aliasInput as HTMLInputElement | undefined;
    const roomName = nameInput?.value.trim();
    const roomTopic = topicTextArea?.value.trim();
    const aliasLocalPart =
      aliasInput && aliasInput.value ? replaceSpaceWithDash(aliasInput.value) : undefined;

    if (!roomName) return;
    const publicRoom = kind === CreateRoomKind.Public;
    let roomKnock = false;
    if (allowKnock && kind === CreateRoomKind.Private) {
      roomKnock = knock;
    }
    if (allowKnockRestricted && kind === CreateRoomKind.Restricted) {
      roomKnock = knock;
    }

    create({
      version: selectedRoomVersion,
      type: spaceType,
      parent: space,
      kind,
      name: roomName,
      topic: roomTopic || undefined,
      aliasLocalPart: publicRoom ? aliasLocalPart : undefined,
      knock: roomKnock,
      allowFederation: federation,
      additionalCreators: allowAdditionalCreators ? additionalCreators : undefined,
    }).then(async (roomId) => {
      if (spaceType === RoomType.Forum) {
        try {
          await mx.sendStateEvent(
            roomId,
            StateEvent.PaarrotRoomKind,
            { kind: 'forum_space' },
            ''
          );
        } catch (err) {
          // eslint-disable-next-line no-console
          console.warn('Failed to set forum space marker state event:', err);
        }
      }

      if (alive()) {
        onCreate?.(roomId);
      }
    });
  };

  return (
    <Box as="form" onSubmit={handleSubmit} grow="Yes" direction="Column" gap="500">
      <Box direction="Column" gap="100">
        <Text size="L400">Access</Text>
        <CreateRoomKindSelector
          value={kind}
          onSelect={setKind}
          canRestrict={allowRestricted}
          disabled={disabled}
          getIcon={getCreateSpaceKindToIcon}
        />
      </Box>
      {!parentIsForum && (
        <Box direction="Column" gap="100">
          <Text size="L400">Type</Text>
          <SequenceCard
            style={{ padding: config.space.S300 }}
            variant="SurfaceVariant"
            direction="Column"
          >
            <SettingTile
              title="Space Type"
              description="Choose standard space navigation or forum-style space behavior."
              after={
                <>
                  <Button
                    size="300"
                    variant="Primary"
                    outlined
                    fill="Soft"
                    radii="300"
                    after={<Icon size="300" src={Icons.ChevronBottom} />}
                    onClick={handleOpenTypeMenu}
                    disabled={disabled}
                  >
                    <Text size="T300">{spaceTypeName[spaceType]}</Text>
                  </Button>
                  <PopOut
                    anchor={typeMenuAnchor}
                    offset={5}
                    position="Bottom"
                    align="End"
                    content={
                      <FocusTrap
                        focusTrapOptions={{
                          initialFocus: false,
                          onDeactivate: () => setTypeMenuAnchor(undefined),
                          clickOutsideDeactivates: true,
                          isKeyForward: (evt: KeyboardEvent) =>
                            evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
                          isKeyBackward: (evt: KeyboardEvent) =>
                            evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
                          escapeDeactivates: stopPropagation,
                        }}
                      >
                        <Menu>
                          <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
                            <MenuItem
                              size="300"
                              variant={spaceType === RoomType.Space ? 'Primary' : 'Surface'}
                              radii="300"
                              onClick={() => handleTypeSelect(RoomType.Space)}
                            >
                              <Text size="T300">Space</Text>
                            </MenuItem>
                            <MenuItem
                              size="300"
                              variant={spaceType === RoomType.Forum ? 'Primary' : 'Surface'}
                              radii="300"
                              onClick={() => handleTypeSelect(RoomType.Forum)}
                            >
                              <Text size="T300">Forum Space</Text>
                            </MenuItem>
                          </Box>
                        </Menu>
                      </FocusTrap>
                    }
                  />
                </>
              }
            />
          </SequenceCard>
        </Box>
      )}
      {parentIsForum && (
        <Text size="T200" priority="300">
          Creates a forum category (subspace). Add topics inside it with Add Topic after selecting the
          category in the filter bar.
        </Text>
      )}
      <Box shrink="No" direction="Column" gap="100">
        <Text size="L400">Name</Text>
        <Input
          required
          before={<Icon size="100" src={getCreateSpaceKindToIcon(kind)} />}
          name="nameInput"
          autoFocus
          size="500"
          variant="SurfaceVariant"
          radii="400"
          autoComplete="off"
          disabled={disabled}
        />
      </Box>
      <Box shrink="No" direction="Column" gap="100">
        <Text size="L400">Topic (Optional)</Text>
        <TextArea
          name="topicTextAria"
          size="500"
          variant="SurfaceVariant"
          radii="400"
          disabled={disabled}
        />
      </Box>

      {kind === CreateRoomKind.Public && <CreateRoomAliasInput disabled={disabled} />}

      <Box shrink="No" direction="Column" gap="100">
        <Box gap="200" alignItems="End">
          <Text size="L400">Options</Text>
          <Box grow="Yes" justifyContent="End">
            <Chip
              radii="Pill"
              before={<Icon src={advance ? Icons.ChevronTop : Icons.ChevronBottom} size="50" />}
              onClick={() => setAdvance(!advance)}
              type="button"
            >
              <Text size="T200">Advance Options</Text>
            </Chip>
          </Box>
        </Box>
        {allowAdditionalCreators && (
          <SequenceCard
            style={{ padding: config.space.S300 }}
            variant="SurfaceVariant"
            direction="Column"
            gap="500"
          >
            <AdditionalCreatorInput
              additionalCreators={additionalCreators}
              onSelect={addAdditionalCreator}
              onRemove={removeAdditionalCreator}
            />
          </SequenceCard>
        )}
        {kind !== CreateRoomKind.Public && advance && (allowKnock || allowKnockRestricted) && (
          <SequenceCard
            style={{ padding: config.space.S300 }}
            variant="SurfaceVariant"
            direction="Column"
            gap="500"
          >
            <SettingTile
              title="Knock to Join"
              description="Anyone can send request to join this space."
              after={
                <Switch variant="Primary" value={knock} onChange={setKnock} disabled={disabled} />
              }
            />
          </SequenceCard>
        )}

        <SequenceCard
          style={{ padding: config.space.S300 }}
          variant="SurfaceVariant"
          direction="Column"
          gap="500"
        >
          <SettingTile
            title="Allow Federation"
            description="Users from other servers can join."
            after={
              <Switch
                variant="Primary"
                value={federation}
                onChange={setFederation}
                disabled={disabled}
              />
            }
          />
        </SequenceCard>
        {advance && (
          <RoomVersionSelector
            versions={roomVersions?.available ? Object.keys(roomVersions.available) : ['1']}
            value={selectedRoomVersion}
            onChange={handleRoomVersionChange}
            disabled={disabled}
          />
        )}
      </Box>

      {error && (
        <Box style={{ color: color.Critical.Main }} alignItems="Center" gap="200">
          <Icon src={Icons.Warning} filled size="100" />
          <Text size="T300" style={{ color: color.Critical.Main }}>
            <b>
              {error instanceof MatrixError && error.name === ErrorCode.M_LIMIT_EXCEEDED
                ? `Server rate-limited your request for ${millisecondsToMinutes(
                    (error.data.retry_after_ms as number | undefined) ?? 0
                  )} minutes!`
                : error.message}
            </b>
          </Text>
        </Box>
      )}
      <Box shrink="No" direction="Column" gap="200">
        <Button
          type="submit"
          size="500"
          variant="Primary"
          radii="400"
          disabled={disabled}
          before={loading && <Spinner variant="Primary" fill="Solid" size="200" />}
        >
          <Text size="B500">Create</Text>
        </Button>
      </Box>
    </Box>
  );
}
