import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Switch, Text } from 'folds';
import { SequenceCard } from '../../../components/sequence-card';
import { SequenceCardStyle } from '../../room-settings/styles.css';
import { SettingTile } from '../../../components/setting-tile';
import { SettingsSelect } from '../../../components/settings-select/SettingsSelect';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useRoom } from '../../../hooks/useRoom';
import { StateEvent } from '../../../../types/matrix/room';
import { sendStateEvent } from '../../../utils/room';
import { useStateEvent } from '../../../hooks/useStateEvent';
import { RoomPermissionsAPI } from '../../../hooks/useRoomPermissions';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import {
  AUDIO_BITRATE_OPTIONS,
  RoomQualityContent,
  SCREENSHARE_BITRATE_OPTIONS,
  SCREENSHARE_FRAMERATE_OPTIONS,
} from '../../../utils/callQuality';

// Only the numeric cap keys are edited via `update`; the boolean policy keys
// are handled by `setAllow`.
type CapKey = 'audio_max_kbps' | 'screenshare_max_kbps' | 'screenshare_max_fps';

// String <-> numeric bridge for SettingsSelect (which needs string values).
const toValue = (n?: number): string => (typeof n === 'number' ? String(n) : 'auto');

const CAP_KEYS: (keyof RoomQualityContent)[] = [
  'audio_max_kbps',
  'screenshare_max_kbps',
  'screenshare_max_fps',
  'allow_screenshare',
  'allow_camera',
];
const capsEqual = (a: RoomQualityContent, b: RoomQualityContent): boolean =>
  CAP_KEYS.every((k) => a[k] === b[k]);

type RoomQualityProps = {
  permissions: RoomPermissionsAPI;
};
/**
 * [P5-31] Room-admin quality ceiling. Writes `io.lotus.room_quality`; every
 * Lotus client clamps its per-user quality to these caps. Hard enforcement for
 * ALL Matrix clients is a server-side follow-up (see LOTUS_TODO.md P5-31).
 */
export function RoomQuality({ permissions }: RoomQualityProps) {
  const mx = useMatrixClient();
  const room = useRoom();

  const canEdit = permissions.stateEvent(StateEvent.LotusRoomQuality, mx.getSafeUserId());

  const event = useStateEvent(room, StateEvent.LotusRoomQuality);
  const caps = useMemo<RoomQualityContent>(() => event?.getContent() ?? {}, [event]);

  const [submitState, submit] = useAsyncCallback(
    useCallback(
      async (next: RoomQualityContent) => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await sendStateEvent(mx, room.roomId, StateEvent.LotusRoomQuality, next);
      },
      [mx, room.roomId],
    ),
  );
  const submitting = submitState.status === AsyncStatus.Loading;

  // Optimistic mirror: `useStateEvent` only refreshes when the write echoes
  // back via /sync (not when sendStateEvent resolves), so consecutive edits
  // must build on the pending write — otherwise a second edit spreads a stale
  // `caps` and silently drops the first. `effective` is what the UI shows and
  // what each edit merges into; it's reconciled below once the echo lands.
  const [pending, setPending] = useState<RoomQualityContent | null>(null);
  const effective = pending ?? caps;

  useEffect(() => {
    if (!pending) return;
    // Revert the optimistic view if the write failed…
    if (submitState.status === AsyncStatus.Error) {
      setPending(null);
      return;
    }
    // …or drop it once the synced state actually reflects it.
    if (capsEqual(caps, pending)) setPending(null);
  }, [caps, pending, submitState.status]);

  const commit = (next: RoomQualityContent) => {
    setPending(next);
    submit(next);
  };

  const update = (key: CapKey, value: string) => {
    const next: RoomQualityContent = { ...effective };
    if (value === 'auto') delete next[key];
    else next[key] = parseInt(value, 10);
    commit(next);
  };

  const setAllow = (key: 'allow_screenshare' | 'allow_camera', allowed: boolean) => {
    const next: RoomQualityContent = { ...effective };
    // Absent = allowed, so only persist the key when forbidding.
    if (allowed) delete next[key];
    else next[key] = false;
    commit(next);
  };

  // Absent/true = allowed.
  const screenshareAllowed = effective.allow_screenshare !== false;
  const cameraAllowed = effective.allow_camera !== false;

  return (
    <SequenceCard
      className={SequenceCardStyle}
      variant="SurfaceVariant"
      direction="Column"
      gap="400"
    >
      <SettingTile
        title="Call Permissions"
        description={
          <Text size="T200" priority="300">
            Control what participants may share in this room. These are enforced on the server for
            every Matrix client (Element, FluffyChat, Lotus Chat, …).
          </Text>
        }
      />
      <Box direction="Column" gap="300">
        <SettingTile
          title="Allow Screen Sharing"
          description="When off, no one can share their screen in this room."
          after={
            <Switch
              variant="Primary"
              value={screenshareAllowed}
              onChange={(v) => setAllow('allow_screenshare', v)}
              disabled={!canEdit || submitting}
            />
          }
        />
        <SettingTile
          title="Allow Camera"
          description="When off, this is an audio-only room — no one can turn on their camera. Microphones are always allowed."
          after={
            <Switch
              variant="Primary"
              value={cameraAllowed}
              onChange={(v) => setAllow('allow_camera', v)}
              disabled={!canEdit || submitting}
            />
          }
        />
      </Box>

      <SettingTile
        title="Call Quality Caps"
        description={
          <Text size="T200" priority="300">
            Set a maximum microphone bitrate, screenshare bitrate, and screenshare framerate for
            this room. Lotus Chat clamps each participant to these ceilings (best-effort — applies
            to Lotus Chat clients). Auto = no cap.
          </Text>
        }
      />
      <Box direction="Column" gap="300">
        <SettingTile
          title="Max Microphone Bitrate"
          after={
            <SettingsSelect
              value={toValue(effective.audio_max_kbps)}
              onChange={(v) => update('audio_max_kbps', v)}
              options={AUDIO_BITRATE_OPTIONS}
              disabled={!canEdit || submitting}
            />
          }
        />
        <SettingTile
          title="Max Screenshare Bitrate"
          after={
            <SettingsSelect
              value={toValue(effective.screenshare_max_kbps)}
              onChange={(v) => update('screenshare_max_kbps', v)}
              options={SCREENSHARE_BITRATE_OPTIONS}
              disabled={!canEdit || submitting}
            />
          }
        />
        <SettingTile
          title="Max Screenshare Framerate"
          after={
            <SettingsSelect
              value={toValue(effective.screenshare_max_fps)}
              onChange={(v) => update('screenshare_max_fps', v)}
              options={SCREENSHARE_FRAMERATE_OPTIONS}
              disabled={!canEdit || submitting}
            />
          }
        />
      </Box>
    </SequenceCard>
  );
}
