import { useCallback, useEffect, useState } from 'react';
import { UserEvent, UserEventHandlerMap } from 'matrix-js-sdk';
import { useMatrixClient } from './useMatrixClient';
import { useMediaAuthentication } from './useMediaAuthentication';
import { mxcUrlToHttp } from '../utils/matrix';import { getCurrentAccessToken } from '../utils/auth';import {
  fetchAndExtractMetadata,
  extractMetadataFromImage,
  embedMetadataInImage,
  detectImageFormat,
  getMimeType,
  getExtension,
  ImageMetadata,
  ProfileGradient,
  convertImageDataToPng,
  uint8ArrayToBlob,
} from '../utils/imageMetadata';

/**
 * Fetches the user's current avatar as raw image data
 */
async function fetchAvatarData(
  mx: ReturnType<typeof useMatrixClient>,
  avatarMxc: string,
  useAuthentication: boolean
): Promise<ArrayBuffer | null> {
  const url = mxcUrlToHttp(mx, avatarMxc, useAuthentication);
  if (!url) return null;

  try {
    // Always use current session's token to avoid stale tokens during account switches
    const accessToken = getCurrentAccessToken();
    let response = await fetch(url, {
      method: 'GET',
      headers: accessToken && useAuthentication ? { Authorization: `Bearer ${accessToken}` } : undefined,
    });
    
    // If we got a 401 and we tried with auth, fallback to unauthenticated request
    if (!response.ok && response.status === 401 && accessToken && useAuthentication) {
      response = await fetch(url, { method: 'GET' });
    }
    
    if (!response.ok) return null;
    return await response.arrayBuffer();
  } catch {
    return null;
  }
}

export type ProfileStyleData = {
  avatarBorderColor?: string;
  gradient?: ProfileGradient;
};

/**
 * Hook to manage the user's profile style (avatar border color and gradient)
 * Stored in avatar image metadata
 * @returns Current style data, a setter function, and loading state
 */
export function useUserProfileStyle(): [
  ProfileStyleData,
  (style: Partial<ProfileStyleData>) => Promise<void>,
  boolean
] {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const [style, setStyle] = useState<ProfileStyleData>({});
  const [loading, setLoading] = useState(true);

  // Extract style from current avatar on mount and when profile changes
  useEffect(() => {
    const loadStyle = async () => {
      setLoading(true);
      try {
        const userId = mx.getUserId();
        if (!userId) {
          setLoading(false);
          return;
        }

        const profile = await mx.getProfileInfo(userId);
        const avatarUrl = profile.avatar_url;

        if (!avatarUrl) {
          setStyle({});
          setLoading(false);
          return;
        }

        const httpUrl = mxcUrlToHttp(mx, avatarUrl, useAuthentication);
        if (!httpUrl) {
          setStyle({});
          setLoading(false);
          return;
        }

        // Always use current session's token to avoid stale tokens during account switches
        const accessToken = getCurrentAccessToken();
        const metadata = await fetchAndExtractMetadata(httpUrl, useAuthentication ? accessToken : null);
        setStyle({
          avatarBorderColor: metadata.avatarBorderColor,
          gradient: metadata.gradient,
        });
      } catch {
        setStyle({});
      }
      setLoading(false);
    };

    loadStyle();

    // Listen for avatar changes and reload style
    const userId = mx.getUserId();
    if (!userId) return undefined;

    const user = mx.getUser(userId);
    const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = () => {
      loadStyle();
    };

    user?.on(UserEvent.AvatarUrl, onAvatarChange);

    return () => {
      user?.removeListener(UserEvent.AvatarUrl, onAvatarChange);
    };
  }, [mx, useAuthentication]);

  const updateStyle = useCallback(
    async (newStyle: Partial<ProfileStyleData>) => {
      const userId = mx.getUserId();
      if (!userId) {
        throw new Error('No user ID');
      }

      const profile = await mx.getProfileInfo(userId);
      const avatarUrl = profile.avatar_url;

      if (!avatarUrl) {
        throw new Error('No avatar set. Please upload an avatar first.');
      }

      // Fetch current avatar
      const avatarData = await fetchAvatarData(mx, avatarUrl, useAuthentication);
      if (!avatarData) {
        throw new Error('Failed to fetch current avatar');
      }

      // Border/gradient live in PNG tEXt — convert other formats first
      let workingData: ArrayBuffer | Uint8Array = avatarData;
      let format = detectImageFormat(workingData);
      if (format === 'unknown') {
        throw new Error('Unsupported avatar image format');
      }

      if (format !== 'png') {
        const pngData = await convertImageDataToPng(workingData);
        if (!pngData) {
          throw new Error('Failed to convert avatar to PNG for style metadata');
        }
        workingData = pngData;
        format = 'png';
      }

      // Get existing metadata to preserve
      const existingMetadata = extractMetadataFromImage(workingData);

      // Merge with new style
      const newMetadata: ImageMetadata = {
        ...existingMetadata,
        avatarBorderColor: newStyle.avatarBorderColor !== undefined ? newStyle.avatarBorderColor : existingMetadata.avatarBorderColor,
        gradient: newStyle.gradient !== undefined ? newStyle.gradient : existingMetadata.gradient,
      };

      // Embed updated metadata
      const newAvatarData = embedMetadataInImage(workingData, newMetadata);
      if (!newAvatarData) {
        throw new Error('Failed to embed style in avatar metadata');
      }

      // Upload modified avatar with correct MIME type
      const mimeType = getMimeType(format);
      const extension = getExtension(format);
      const blob = uint8ArrayToBlob(newAvatarData, mimeType);

      const uploadResponse = await mx.uploadContent(blob, {
        name: `avatar.${extension}`,
        type: mimeType,
      });

      // Update profile with new avatar
      await mx.setAvatarUrl(uploadResponse.content_uri);

      // Manually sync user object
      const user = mx.getUser(userId);
      if (user && user.avatarUrl !== uploadResponse.content_uri) {
        user.setAvatarUrl(uploadResponse.content_uri);
      }

      // Update local state
      setStyle((prev) => ({
        ...prev,
        ...newStyle,
      }));
    },
    [mx, useAuthentication]
  );

  return [style, updateStyle, loading];
}

// Cache for other users' profile styles
const userStyleCache = new Map<string, { style: ProfileStyleData; timestamp: number }>();
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes

/**
 * Hook to get another user's profile style from their avatar metadata
 * @param userId - The user ID to get style for
 * @param avatarMxc - The user's avatar MXC URL
 * @returns The user's profile style data
 */
export function useOtherUserProfileStyle(userId: string, avatarMxc: string | undefined): ProfileStyleData {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const [style, setStyle] = useState<ProfileStyleData>({});

  useEffect(() => {
    if (!avatarMxc) {
      console.log('[useOtherUserProfileStyle] No avatarMxc provided for', userId);
      setStyle({});
      return;
    }

    const loadStyle = async () => {
      console.log('[useOtherUserProfileStyle] Loading style for', userId, 'avatarMxc:', avatarMxc);
      
      // Check cache first
      const cacheKey = `${userId}:${avatarMxc}`;
      const cached = userStyleCache.get(cacheKey);

      if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
        console.log('[useOtherUserProfileStyle] Using cached style:', cached.style);
        setStyle(cached.style);
        return;
      }

      const httpUrl = mxcUrlToHttp(mx, avatarMxc, useAuthentication);
      console.log('[useOtherUserProfileStyle] Fetching metadata from:', httpUrl);
      
      if (!httpUrl) {
        console.log('[useOtherUserProfileStyle] Failed to get HTTP URL');
        setStyle({});
        return;
      }

      // Always use current session's token to avoid stale tokens during account switches
      const accessToken = getCurrentAccessToken();
      const metadata = await fetchAndExtractMetadata(httpUrl, useAuthentication ? accessToken : null);

      console.log('[useOtherUserProfileStyle] Extracted metadata:', metadata);

      const styleData: ProfileStyleData = {
        avatarBorderColor: metadata.avatarBorderColor,
        gradient: metadata.gradient,
      };

      console.log('[useOtherUserProfileStyle] Final styleData:', styleData);

      // Cache the result
      userStyleCache.set(cacheKey, { style: styleData, timestamp: Date.now() });
      setStyle(styleData);
    };

    loadStyle();
  }, [mx, useAuthentication, avatarMxc, userId]);

  return style;
}
