import { Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk';
import { useCallback, useEffect, useState } from 'react';
import { useMatrixClient } from './useMatrixClient';
import { useStateEvent } from './useStateEvent';
import { StateEvent } from '../../types/matrix/room';

/**
 * The room account data type for storing personal embed filter patterns.
 */
export const EMBED_FILTERS_ACCOUNT_DATA_TYPE = 'im.paarrot.embed_filters';

/**
 * Content structure for embed filter data.
 */
export interface EmbedFiltersContent {
  /**
   * Array of regex pattern strings to match URLs that should not show embeds.
   * Each pattern will be tested against URLs using RegExp.test().
   */
  disabledPatterns: string[];
}

/**
 * Default empty embed filters content.
 */
const DEFAULT_EMBED_FILTERS: EmbedFiltersContent = {
  disabledPatterns: [],
};

/**
 * Gets the personal embed filters content from room account data.
 * @param room - The Matrix room to get filters for
 * @returns The embed filters content
 */
const getPersonalEmbedFiltersFromRoom = (room: Room): EmbedFiltersContent => {
  const accountDataEvent = room.getAccountData(EMBED_FILTERS_ACCOUNT_DATA_TYPE);
  if (!accountDataEvent) {
    return DEFAULT_EMBED_FILTERS;
  }

  const content = accountDataEvent.getContent() as Partial<EmbedFiltersContent>;
  return {
    disabledPatterns: Array.isArray(content.disabledPatterns) ? content.disabledPatterns : [],
  };
};

/**
 * Hook to get and set personal embed filter patterns for a room.
 * These patterns are stored in room account data (per-user, per-room).
 * @param room - The Matrix room
 * @returns A tuple of [filters, setFilters]
 */
export const useRoomEmbedFilters = (
  room: Room
): [EmbedFiltersContent, (filters: EmbedFiltersContent) => Promise<void>] => {
  const mx = useMatrixClient();
  const [filters, setFiltersState] = useState<EmbedFiltersContent>(() =>
    getPersonalEmbedFiltersFromRoom(room)
  );

  useEffect(() => {
    const handleAccountData: RoomEventHandlerMap[RoomEvent.AccountData] = (event) => {
      if (event.getType() === EMBED_FILTERS_ACCOUNT_DATA_TYPE) {
        setFiltersState(getPersonalEmbedFiltersFromRoom(room));
      }
    };

    room.on(RoomEvent.AccountData, handleAccountData);
    return () => {
      room.removeListener(RoomEvent.AccountData, handleAccountData);
    };
  }, [room]);

  const setFilters = useCallback(
    async (newFilters: EmbedFiltersContent): Promise<void> => {
      await mx.setRoomAccountData(room.roomId, EMBED_FILTERS_ACCOUNT_DATA_TYPE, newFilters);
    },
    [mx, room.roomId]
  );

  return [filters, setFilters];
};

/**
 * Hook to get room-wide embed filter patterns set by room admins.
 * These patterns are stored as room state and apply to all users.
 * @param room - The Matrix room
 * @returns The room-wide embed filters content
 */
export const useRoomWideEmbedFilters = (room: Room): EmbedFiltersContent => {
  const stateEvent = useStateEvent(room, StateEvent.RoomEmbedFilters);
  if (!stateEvent) {
    return DEFAULT_EMBED_FILTERS;
  }

  const content = stateEvent.getContent() as Partial<EmbedFiltersContent>;
  return {
    disabledPatterns: Array.isArray(content.disabledPatterns) ? content.disabledPatterns : [],
  };
};

/**
 * Hook to set room-wide embed filter patterns.
 * Requires appropriate power level to send state events.
 * @param room - The Matrix room
 * @returns Function to save room-wide filters
 */
export const useSetRoomWideEmbedFilters = (
  room: Room
): ((filters: EmbedFiltersContent) => Promise<void>) => {
  const mx = useMatrixClient();

  const setFilters = useCallback(
    async (newFilters: EmbedFiltersContent): Promise<void> => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      await mx.sendStateEvent(room.roomId, StateEvent.RoomEmbedFilters as any, newFilters);
    },
    [mx, room.roomId]
  );

  return setFilters;
};

/**
 * Combines personal and room-wide embed filter patterns.
 * @param personalFilters - Personal (per-user) filters
 * @param roomWideFilters - Room-wide (admin-set) filters
 * @returns Combined unique patterns
 */
export const combineEmbedFilters = (
  personalFilters: EmbedFiltersContent,
  roomWideFilters: EmbedFiltersContent
): string[] => {
  const combined = new Set<string>([
    ...personalFilters.disabledPatterns,
    ...roomWideFilters.disabledPatterns,
  ]);
  return Array.from(combined);
};

/**
 * Tests if a URL should have its embed disabled based on the filter patterns.
 * @param url - The URL to test
 * @param disabledPatterns - Array of regex pattern strings
 * @returns True if the URL matches any disabled pattern
 */
export const isUrlEmbedDisabled = (url: string, disabledPatterns: string[]): boolean => {
  if (!disabledPatterns || disabledPatterns.length === 0) {
    return false;
  }

  return disabledPatterns.some((pattern) => {
    try {
      const regex = new RegExp(pattern, 'i');
      return regex.test(url);
    } catch {
      // Invalid regex pattern, skip it
      return false;
    }
  });
};

/**
 * Filters an array of URLs, removing those that match any disabled pattern.
 * @param urls - Array of URLs to filter
 * @param disabledPatterns - Array of regex pattern strings
 * @returns Array of URLs that don't match any disabled pattern
 */
export const filterDisabledUrls = (urls: string[], disabledPatterns: string[]): string[] => {
  if (!disabledPatterns || disabledPatterns.length === 0) {
    return urls;
  }

  return urls.filter((url) => !isUrlEmbedDisabled(url, disabledPatterns));
};
