import React, { FormEventHandler, useCallback, useEffect, useState } from 'react';
import { MatrixError, Room, JoinRule } from 'matrix-js-sdk';
import {
  Box,
  Button,
  Chip,
  color,
  config,
  Icon,
  IconButton,
  Icons,
  Input,
  PopOut,
  RectCords,
  Spinner,
  Switch,
  Text,
  TextArea,
} from 'folds';
import { EmojiBoard } from '../../components/emoji-board';
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,
  CreateRoomAccess,
  CreateRoomAccessSelector,
  RoomVersionSelector,
  useAdditionalCreators,
  CreateRoomType,
} from '../../components/create-room';
import { RoomType } from '../../../types/matrix/room';
import { CreateRoomTypeSelector } from '../../components/create-room/CreateRoomTypeSelector';
import { getRoomIconSrc } from '../../utils/room';

const getCreateRoomAccessToIcon = (access: CreateRoomAccess, type?: CreateRoomType) => {
  const isVoiceRoom = type === CreateRoomType.VoiceRoom;

  let joinRule: JoinRule = JoinRule.Public;
  if (access === CreateRoomAccess.Restricted) joinRule = JoinRule.Restricted;
  if (access === CreateRoomAccess.Private) joinRule = JoinRule.Knock;

  return getRoomIconSrc(Icons, isVoiceRoom ? RoomType.Call : undefined, joinRule);
};

const getCreateRoomTypeToIcon = (type: CreateRoomType) => {
  if (type === CreateRoomType.VoiceRoom) return Icons.VolumeHigh;
  return Icons.Hash;
};

type CreateRoomFormProps = {
  defaultAccess?: CreateRoomAccess;
  defaultType?: CreateRoomType;
  space?: Room;
  onCreate?: (roomId: string) => void;
};
export function CreateRoomForm({
  defaultAccess,
  defaultType,
  space,
  onCreate,
}: CreateRoomFormProps) {
  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 [type, setType] = useState(defaultType ?? CreateRoomType.TextRoom);
  const [access, setAccess] = useState(
    defaultAccess ?? (allowRestricted ? CreateRoomAccess.Restricted : CreateRoomAccess.Private),
  );
  const allowAdditionalCreators = creatorsSupported(selectedRoomVersion);
  const { additionalCreators, addAdditionalCreator, removeAdditionalCreator } =
    useAdditionalCreators();
  const [federation, setFederation] = useState(true);
  const [encryption, setEncryption] = useState(false);
  const [knock, setKnock] = useState(false);
  const [advance, setAdvance] = useState(false);
  const [nameValue, setNameValue] = useState('');
  const [emojiAnchor, setEmojiAnchor] = useState<RectCords>();

  const handleEmojiSelect = useCallback((unicode: string) => {
    // Clamp to the same cap as the input's maxLength — programmatic setState isn't
    // constrained by the DOM maxLength, so the picker could otherwise exceed it.
    setNameValue((prev) => (unicode + prev).slice(0, 255));
    setEmojiAnchor(undefined);
  }, []);

  const allowKnock = access === CreateRoomAccess.Private && knockSupported(selectedRoomVersion);
  const allowKnockRestricted =
    access === CreateRoomAccess.Restricted && knockRestrictedSupported(selectedRoomVersion);

  const handleRoomVersionChange = (version: string) => {
    if (!restrictedSupported(version)) {
      setAccess(CreateRoomAccess.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 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 = access === CreateRoomAccess.Public;
    let roomKnock = false;
    if (allowKnock && access === CreateRoomAccess.Private) {
      roomKnock = knock;
    }
    if (allowKnockRestricted && access === CreateRoomAccess.Restricted) {
      roomKnock = knock;
    }

    let roomType: RoomType | undefined;
    if (type === CreateRoomType.VoiceRoom) roomType = RoomType.Call;

    create({
      version: selectedRoomVersion,
      type: roomType,
      parent: space,
      access,
      name: roomName,
      topic: roomTopic || undefined,
      aliasLocalPart: publicRoom ? aliasLocalPart : undefined,
      encryption: publicRoom ? false : encryption,
      knock: roomKnock,
      allowFederation: federation,
      additionalCreators: allowAdditionalCreators ? additionalCreators : undefined,
    }).then((roomId) => {
      if (alive()) {
        onCreate?.(roomId);
      }
    });
  };

  return (
    <Box as="form" onSubmit={handleSubmit} grow="Yes" direction="Column" gap="500">
      {!space && (
        <Box direction="Column" gap="100">
          <Text size="L400">Type</Text>
          <CreateRoomTypeSelector
            value={type}
            onSelect={setType}
            disabled={disabled}
            getIcon={getCreateRoomTypeToIcon}
          />
        </Box>
      )}
      <Box direction="Column" gap="100">
        <Text size="L400">Access</Text>
        <CreateRoomAccessSelector
          value={access}
          onSelect={setAccess}
          canRestrict={allowRestricted}
          disabled={disabled}
          getIcon={(roomAccess) => getCreateRoomAccessToIcon(roomAccess, type)}
        />
      </Box>
      <Box shrink="No" direction="Column" gap="100">
        <Text size="L400">Name</Text>
        <Box direction="Row" gap="100" alignItems="Center">
          <PopOut
            anchor={emojiAnchor}
            position="Top"
            align="End"
            content={
              <EmojiBoard
                imagePackRooms={[]}
                returnFocusOnDeactivate={false}
                onEmojiSelect={handleEmojiSelect}
                requestClose={() => setEmojiAnchor(undefined)}
              />
            }
          >
            <IconButton
              type="button"
              size="400"
              radii="400"
              variant="Surface"
              fill="Soft"
              outlined
              disabled={disabled}
              aria-label="Insert emoji"
              aria-expanded={!!emojiAnchor}
              aria-haspopup="dialog"
              onClick={(evt: React.MouseEvent<HTMLButtonElement>) => {
                const rect = evt.currentTarget.getBoundingClientRect();
                setEmojiAnchor((prev) => (prev ? undefined : rect));
              }}
            >
              <Icon src={Icons.Smile} size="400" />
            </IconButton>
          </PopOut>
          <Box grow="Yes">
            <Input
              required
              before={<Icon size="100" src={getCreateRoomAccessToIcon(access, type)} />}
              name="nameInput"
              autoFocus
              size="500"
              variant="SurfaceVariant"
              radii="400"
              autoComplete="off"
              disabled={disabled}
              value={nameValue}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => setNameValue(e.target.value)}
              maxLength={255}
              style={{ width: '100%' }}
            />
          </Box>
        </Box>
      </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>

      {access === CreateRoomAccess.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">Advanced 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>
        )}
        {access !== CreateRoomAccess.Public && (
          <>
            <SequenceCard
              style={{ padding: config.space.S300 }}
              variant="SurfaceVariant"
              direction="Column"
              gap="500"
            >
              <SettingTile
                title="End-to-End Encryption"
                description="Once this feature is enabled, it can't be disabled after the room is created."
                after={
                  <Switch
                    variant="Primary"
                    value={encryption}
                    onChange={setEncryption}
                    disabled={disabled}
                  />
                }
              />
            </SequenceCard>
            {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 room."
                  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>
  );
}
