import React, { useCallback, useMemo, useState } from 'react';
import { Box, Button, IconButton, Scroll, Spinner, Text, color } from 'folds';
import { Icon, Icons } from '../../../components/icons';
import { useAtomValue } from 'jotai';
import { Page, PageContent, PageHeader } from '../../../components/page';
import { useRoom } from '../../../hooks/useRoom';
import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { StateEvent } from '../../../../types/matrix/room';
import { usePermissionGroups } from './usePermissionItems';
import { PermissionGroups, Powers, PowersEditor } from '../../common-settings/permissions';
import { useRoomCreators } from '../../../hooks/useRoomCreators';
import { useRoomPermissions } from '../../../hooks/useRoomPermissions';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { useRecursiveChildScopeFactory, useRecursiveChildSpaceScopeFactory, useSpaceChildren } from '../../../state/hooks/roomList';

type PermissionsProps = {
  requestClose: () => void;
};
export function Permissions({ requestClose }: PermissionsProps) {
  const mx = useMatrixClient();
  const room = useRoom();
  const powerLevels = usePowerLevels(room);
  const creators = useRoomCreators(room);
  const roomToParents = useAtomValue(roomToParentsAtom);

  const permissions = useRoomPermissions(creators, powerLevels);

  const canEditPowers = permissions.stateEvent(StateEvent.PowerLevelTags, mx.getSafeUserId());
  const canEditPermissions = permissions.stateEvent(StateEvent.RoomPowerLevels, mx.getSafeUserId());
  const permissionGroups = usePermissionGroups();

  const [powerEditor, setPowerEditor] = useState(false);

  // Get all child spaces recursively
  const childSpaces = useSpaceChildren(
    allRoomsAtom,
    room.roomId,
    useRecursiveChildSpaceScopeFactory(mx, roomToParents)
  );

  // Get all child rooms recursively
  const childRooms = useSpaceChildren(
    allRoomsAtom,
    room.roomId,
    useRecursiveChildScopeFactory(mx, roomToParents)
  );

  // Combine both spaces and rooms for syncing
  const allChildren = [...childSpaces, ...childRooms];

  // Calculate which children we can sync to
  const { syncableChildren, nonSyncableChildren } = useMemo(() => {
    const syncable: string[] = [];
    const nonSyncable: { roomId: string; name: string }[] = [];

    allChildren.forEach((childId) => {
      const childRoom = mx.getRoom(childId);
      if (!childRoom) {
        nonSyncable.push({ roomId: childId, name: childId });
        return;
      }

      // Check if we have permission to edit power levels in the child
      const childPowerLevels = childRoom.currentState.getStateEvents(StateEvent.RoomPowerLevels, '');
      const childPowerLevelsContent = childPowerLevels?.getContent() || {};
      const childCreators = new Set<string>();
      const creatorId = childRoom.currentState.getStateEvents('m.room.create', '')?.getContent()?.creator;
      if (creatorId) childCreators.add(creatorId);

      const myUserId = mx.getSafeUserId();
      const myPower = childPowerLevelsContent.users?.[myUserId] ?? childPowerLevelsContent.users_default ?? 0;
      
      // Check the specific power level required for m.room.power_levels events
      const requiredPower = childPowerLevelsContent.events?.[StateEvent.RoomPowerLevels] ?? 
                           childPowerLevelsContent.state_default ?? 50;

      if (myPower >= requiredPower || childCreators.has(myUserId)) {
        syncable.push(childId);
      } else {
        nonSyncable.push({ roomId: childId, name: childRoom.name || childId });
      }
    });

    return { syncableChildren: syncable, nonSyncableChildren: nonSyncable };
  }, [allChildren, mx]);

  const [syncState, syncPermissions] = useAsyncCallback(
    useCallback(async () => {
      // Get the power level tags from this space
      const powerLevelTagsEvent = room.currentState.getStateEvents('in.cinny.room.power_level_tags', '');
      const powerLevelTags = powerLevelTagsEvent?.getContent();

      let completed = 0;
      const total = syncableChildren.length;
      const myUserId = mx.getSafeUserId();

      for (const childId of syncableChildren) {
        try {
          const childRoom = mx.getRoom(childId);
          if (!childRoom) continue;

          // Get current power levels in the child room
          const childPowerLevelsEvent = childRoom.currentState.getStateEvents(StateEvent.RoomPowerLevels, '');
          const childPowerLevels = childPowerLevelsEvent?.getContent() || {};
          
          // Get my power level in the child room
          const myPowerInChild = childPowerLevels.users?.[myUserId] ?? childPowerLevels.users_default ?? 0;
          
          // Build merged users: only sync users with power <= mine, preserve higher-powered users
          const mergedUsers: Record<string, number> = {};
          
          // Copy users from parent where power <= my power in child
          if (powerLevels.users) {
            Object.entries(powerLevels.users).forEach(([userId, power]) => {
              if (power <= myPowerInChild) {
                mergedUsers[userId] = power;
              }
            });
          }
          
          // Preserve all users in child room who have power >= mine
          if (childPowerLevels.users) {
            Object.entries(childPowerLevels.users).forEach(([userId, power]) => {
              if (power >= myPowerInChild) {
                mergedUsers[userId] = power;
              }
            });
          }
          
          // Cap all power level values at my power level
          const capPowerLevel = (value: any) => {
            if (typeof value === 'number') {
              return Math.min(value, myPowerInChild);
            }
            return value;
          };
          
          // Create the merged power levels with capped values
          const mergedPowerLevels: any = {
            ...powerLevels,
            users: mergedUsers,
            ban: capPowerLevel(powerLevels.ban),
            kick: capPowerLevel(powerLevels.kick),
            redact: capPowerLevel(powerLevels.redact),
            invite: capPowerLevel(powerLevels.invite),
            events_default: capPowerLevel(powerLevels.events_default),
            state_default: capPowerLevel(powerLevels.state_default),
            users_default: capPowerLevel(powerLevels.users_default),
          };
          
          // Cap event-specific power levels
          if (powerLevels.events) {
            mergedPowerLevels.events = {};
            Object.entries(powerLevels.events).forEach(([eventType, power]) => {
              mergedPowerLevels.events[eventType] = capPowerLevel(power);
            });
          }
          
          // Cap notification power levels
          if (powerLevels.notifications) {
            mergedPowerLevels.notifications = {};
            Object.entries(powerLevels.notifications).forEach(([key, power]) => {
              mergedPowerLevels.notifications[key] = capPowerLevel(power);
            });
          }
          
          await mx.sendStateEvent(childId, StateEvent.RoomPowerLevels as any, mergedPowerLevels);
          
          // Also copy power level tags if they exist
          if (powerLevelTags) {
            await mx.sendStateEvent(childId, 'in.cinny.room.power_level_tags' as any, powerLevelTags);
          }
          
          completed += 1;
          
          // Add a small delay to avoid rate limiting
          if (completed < total) {
            await new Promise(resolve => setTimeout(resolve, 100));
          }
        } catch (error) {
          console.error(`Failed to sync permissions to ${childId}:`, error);
        }
      }

      return { completed, total };
    }, [mx, syncableChildren, powerLevels, room])
  );

  const [syncPowerLevelsState, syncPowerLevelsOnly] = useAsyncCallback(
    useCallback(async () => {
      let completed = 0;
      const total = syncableChildren.length;
      const myUserId = mx.getSafeUserId();

      for (const childId of syncableChildren) {
        try {
          const childRoom = mx.getRoom(childId);
          if (!childRoom) continue;

          // Get current power levels in the child room
          const childPowerLevelsEvent = childRoom.currentState.getStateEvents(StateEvent.RoomPowerLevels, '');
          const childPowerLevels = childPowerLevelsEvent?.getContent() || {};
          
          // Get my power level in the child room
          const myPowerInChild = childPowerLevels.users?.[myUserId] ?? childPowerLevels.users_default ?? 0;
          
          // Build merged users: only sync users with power <= mine, preserve higher-powered users
          const mergedUsers: Record<string, number> = {};
          
          // Copy users from parent where power <= my power in child
          if (powerLevels.users) {
            Object.entries(powerLevels.users).forEach(([userId, power]) => {
              if (power <= myPowerInChild) {
                mergedUsers[userId] = power;
              }
            });
          }
          
          // Preserve all users in child room who have power >= mine
          if (childPowerLevels.users) {
            Object.entries(childPowerLevels.users).forEach(([userId, power]) => {
              if (power >= myPowerInChild) {
                mergedUsers[userId] = power;
              }
            });
          }
          
          // Cap all power level values at my power level
          const capPowerLevel = (value: any) => {
            if (typeof value === 'number') {
              return Math.min(value, myPowerInChild);
            }
            return value;
          };
          
          // Create the merged power levels with capped values
          const mergedPowerLevels: any = {
            ...powerLevels,
            users: mergedUsers,
            ban: capPowerLevel(powerLevels.ban),
            kick: capPowerLevel(powerLevels.kick),
            redact: capPowerLevel(powerLevels.redact),
            invite: capPowerLevel(powerLevels.invite),
            events_default: capPowerLevel(powerLevels.events_default),
            state_default: capPowerLevel(powerLevels.state_default),
            users_default: capPowerLevel(powerLevels.users_default),
          };
          
          // Cap event-specific power levels
          if (powerLevels.events) {
            mergedPowerLevels.events = {};
            Object.entries(powerLevels.events).forEach(([eventType, power]) => {
              mergedPowerLevels.events[eventType] = capPowerLevel(power);
            });
          }
          
          // Cap notification power levels
          if (powerLevels.notifications) {
            mergedPowerLevels.notifications = {};
            Object.entries(powerLevels.notifications).forEach(([key, power]) => {
              mergedPowerLevels.notifications[key] = capPowerLevel(power);
            });
          }
          
          await mx.sendStateEvent(childId, StateEvent.RoomPowerLevels as any, mergedPowerLevels);
          
          completed += 1;
          
          // Add a small delay to avoid rate limiting
          if (completed < total) {
            await new Promise(resolve => setTimeout(resolve, 100));
          }
        } catch (error) {
          console.error(`Failed to sync power levels to ${childId}:`, error);
        }
      }

      return { completed, total };
    }, [mx, syncableChildren, powerLevels])
  );

  const handleEditPowers = () => {
    setPowerEditor(true);
  };

  const handleSyncPermissions = () => {
    syncPermissions();
  };

  const handleSyncPowerLevelsOnly = () => {
    syncPowerLevelsOnly();
  };

  if (canEditPowers && powerEditor) {
    return <PowersEditor powerLevels={powerLevels} requestClose={() => setPowerEditor(false)} />;
  }

  const hasSyncableChildren = allChildren.length > 0;
  const isSyncing = syncState.status === AsyncStatus.Loading;
  const syncSuccess = syncState.status === AsyncStatus.Success;
  const syncError = syncState.status === AsyncStatus.Error;
  const isPowerLevelsSyncing = syncPowerLevelsState.status === AsyncStatus.Loading;
  const powerLevelsSyncSuccess = syncPowerLevelsState.status === AsyncStatus.Success;
  const powerLevelsSyncError = syncPowerLevelsState.status === AsyncStatus.Error;

  return (
    <Page>
      <PageHeader outlined={false}>
        <Box grow="Yes" gap="200">
          <Box grow="Yes" alignItems="Center" gap="200">
            <Text size="H3" truncate>
              Permissions
            </Text>
          </Box>
          <Box shrink="No">
            <IconButton onClick={requestClose} variant="Surface">
              <Icon src={Icons.Cross} />
            </IconButton>
          </Box>
        </Box>
      </PageHeader>
      <Box grow="Yes">
        <Scroll hideTrack visibility="Hover">
          <PageContent>
            <Box direction="Column" gap="700">
              {syncableChildren.length > 0 && (
                <Box direction="Column" gap="100">
                  <Text size="L400">Sync Permissions</Text>
                  <Box direction="Column" gap="300" style={{ padding: 'var(--sp-300)' }}>
                    <Text size="T300">
                      Sync these permissions and power level tags to all child subspaces and rooms.
                      {nonSyncableChildren.length > 0 ? (
                        <> This will sync to {syncableChildren.length} child{' '}
                          {syncableChildren.length === 1 ? 'space/room' : 'spaces/rooms'} where you have permission.
                          <Text as="span" style={{ color: color.Warning.Main }}>
                            {' '}You don't have permission to edit {nonSyncableChildren.length} other{' '}
                            {nonSyncableChildren.length === 1 ? 'space/room' : 'spaces/rooms'}
                            {nonSyncableChildren.length <= 5 && (
                              <>: {nonSyncableChildren.map(c => c.name).join(', ')}</>
                            )}.
                          </Text>
                        </>
                      ) : (
                        <> This will overwrite the power levels and custom role tags in {syncableChildren.length} child{' '}
                          {syncableChildren.length === 1 ? 'space/room' : 'spaces/rooms'}.
                        </>
                      )}
                    </Text>
                    <Box direction="Column" gap="200">
                      <Box gap="200" alignItems="Center">
                        <Button
                          variant="Critical"
                          onClick={handleSyncPermissions}
                          disabled={isSyncing || isPowerLevelsSyncing}
                          before={
                            isSyncing ? (
                              <Spinner variant="Critical" fill="Solid" size="50" />
                            ) : (
                              <Icon src={Icons.Download} size="50" />
                            )
                          }
                        >
                          <Text size="B400">
                            {isSyncing
                              ? 'Syncing All...'
                              : `Sync All to ${syncableChildren.length} ${
                                  syncableChildren.length === 1 ? 'Child' : 'Children'
                                }${nonSyncableChildren.length > 0 ? ` (${nonSyncableChildren.length} skipped)` : ''}`}
                          </Text>
                        </Button>
                        {syncSuccess && (
                          <Text size="T300" style={{ color: color.Success.Main }}>
                            Successfully synced permissions to {syncState.data.completed} of{' '}
                            {syncState.data.total} children
                          </Text>
                        )}
                        {syncError && (
                          <Text size="T300" style={{ color: color.Critical.Main }}>
                            Error syncing permissions: {syncState.error.message}
                          </Text>
                        )}
                      </Box>
                      <Box gap="200" alignItems="Center">
                        <Button
                          variant="Secondary"
                          onClick={handleSyncPowerLevelsOnly}
                          disabled={isSyncing || isPowerLevelsSyncing}
                          before={
                            isPowerLevelsSyncing ? (
                              <Spinner variant="Secondary" fill="Solid" size="50" />
                            ) : (
                              <Icon src={Icons.Download} size="50" />
                            )
                          }
                        >
                          <Text size="B400">
                            {isPowerLevelsSyncing
                              ? 'Syncing Power Levels...'
                              : `Sync Power Levels Only to ${syncableChildren.length} ${
                                  syncableChildren.length === 1 ? 'Child' : 'Children'
                                }${nonSyncableChildren.length > 0 ? ` (${nonSyncableChildren.length} skipped)` : ''}`}
                          </Text>
                        </Button>
                        {powerLevelsSyncSuccess && (
                          <Text size="T300" style={{ color: color.Success.Main }}>
                            Successfully synced power levels to {syncPowerLevelsState.data.completed} of{' '}
                            {syncPowerLevelsState.data.total} children
                          </Text>
                        )}
                        {powerLevelsSyncError && (
                          <Text size="T300" style={{ color: color.Critical.Main }}>
                            Error syncing power levels: {syncPowerLevelsState.error.message}
                          </Text>
                        )}
                      </Box>
                    </Box>
                  </Box>
                </Box>
              )}
              <Powers
                powerLevels={powerLevels}
                onEdit={canEditPowers ? handleEditPowers : undefined}
                permissionGroups={permissionGroups}
              />
              <PermissionGroups
                canEdit={canEditPermissions}
                powerLevels={powerLevels}
                permissionGroups={permissionGroups}
              />
            </Box>
          </PageContent>
        </Scroll>
      </Box>
    </Page>
  );
}
