import React, { ReactNode, useMemo, useEffect, useState, useCallback, useRef } from 'react';
import { Box, Text, IconButton } from 'folds';
import { invoke } from '@tauri-apps/api/core';
import { MatrixClient, RoomEvent, ClientEvent, MatrixEvent, Room, IPushRules, SyncState } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { WindowControls } from './WindowControls';
import { UpdateNotification } from '../update-notification';
import * as css from './TitleBar.css';
import { getUnreadInfos, getOrphanParents, guessPerfectParent, getAllParents } from '../../utils/room';
import { getCanonicalAliasOrRoomId, getMxIdLocalPart } from '../../utils/matrix';
import { activeRoomIdAtom } from '../../state/activeRoom';
import { roomToParentsAtom } from '../../state/room/roomToParents';
import { mDirectAtom } from '../../state/mDirectList';
import { getDirectRoomPath, getHomeRoomPath, getSpaceRoomPath } from '../../pages/pathUtils';
import { RoomNotificationMode } from '../../hooks/useRoomsNotificationPreferences';
import { AccountDataEvent } from '../../../types/matrix/accountData';
import { getNotificationMode, NotificationMode } from '../../hooks/useNotificationMode';
import { isRoomId } from '../../utils/matrix';
import { useSyncState } from '../../hooks/useSyncState';
import { useBackRoute } from '../../hooks/useBackRoute';
import { Icon, Icons } from '../icons';

type NewMessageNotification = {
  id: string;
  roomId: string;
  eventId: string;
  senderName: string;
  preview: string;
  exiting?: boolean;
};

export type TitleBarProps = {
  mx?: MatrixClient;
  children?: ReactNode;
};

export function TitleBar({ mx, children }: TitleBarProps) {
  const [updateCounter, setUpdateCounter] = useState(0);
  const roomId = useAtomValue(activeRoomIdAtom);
  const [notifications, setNotifications] = useState<NewMessageNotification[]>([]);
  const dismissTimersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
  const lastMxRef = useRef<MatrixClient | undefined>();
  const navigate = useNavigate();
  const roomToParents = useAtomValue(roomToParentsAtom);
  const mDirects = useAtomValue(mDirectAtom);
  const { showInTitleBar, goBack } = useBackRoute(mx);

  // Sync status state
  const [syncStateData, setSyncStateData] = useState<{
    current: SyncState | null;
    previous: SyncState | null | undefined;
  }>({
    current: null,
    previous: undefined,
  });

  // Track sync state changes
  useSyncState(
    mx,
    useCallback((current, previous) => {
      setSyncStateData((s) => {
        if (s.current === current && s.previous === previous) {
          return s;
        }
        return { current, previous };
      });
    }, [])
  );

  // Notification-related state - only populated when mx is available
  const [notificationData, setNotificationData] = useState<{
    preferences: { mute: Set<string>; specialMessages: Set<string>; allMessages: Set<string> };
    keywordPatterns: string[];
    userId: string | undefined;
    displayName: string | undefined;
  }>({
    preferences: { mute: new Set(), specialMessages: new Set(), allMessages: new Set() },
    keywordPatterns: [],
    userId: undefined,
    displayName: undefined,
  });

  // Update notification data when mx is available (only when mx changes)
  useEffect(() => {
    // Only update if mx actually changed
    if (lastMxRef.current === mx) return;
    lastMxRef.current = mx;

    if (!mx) {
      setNotificationData({
        preferences: { mute: new Set(), specialMessages: new Set(), allMessages: new Set() },
        keywordPatterns: [],
        userId: undefined,
        displayName: undefined,
      });
      return;
    }

    // We can't use hooks here, so we'll fetch the data directly
    const userId = mx.getUserId();
    
    // Get push rules for keywords
    const pushRulesEvt = mx.getAccountData(AccountDataEvent.PushRules);
    const pushRules = pushRulesEvt?.getContent<IPushRules>() ?? { global: {} };
    const content = pushRules.global.content ?? [];
    const keywordPatterns = content
      .filter((rule: any) => rule.default === false && typeof rule.pattern === 'string')
      .map((rule: any) => rule.pattern as string);

    // Get notification preferences
    const global = pushRules?.global;
    const room = global?.room ?? [];
    const override = global?.override ?? [];

    const preferences = {
      mute: new Set<string>(),
      specialMessages: new Set<string>(),
      allMessages: new Set<string>(),
    };

    // Check override rules for muted rooms
    override.forEach((rule: any) => {
      if (isRoomId(rule.rule_id) && getNotificationMode(rule.actions) === NotificationMode.OFF) {
        preferences.mute.add(rule.rule_id);
      }
    });
    
    // Check room rules for special messages (OFF mode = mentions only)
    room.forEach((rule: any) => {
      if (getNotificationMode(rule.actions) === NotificationMode.OFF) {
        preferences.specialMessages.add(rule.rule_id);
      }
    });
    
    // Check room rules for all messages (Notify or NotifyLoud mode)
    room.forEach((rule: any) => {
      if (getNotificationMode(rule.actions) !== NotificationMode.OFF) {
        preferences.allMessages.add(rule.rule_id);
      }
    });

    // Get display name
    const user = userId ? mx.getUser(userId) : null;
    const displayName = user?.displayName;

    console.log('[TitleBar] Notification preferences loaded:', {
      muted: Array.from(preferences.mute),
      specialMessages: Array.from(preferences.specialMessages),
      allMessages: Array.from(preferences.allMessages),
      keywordPatterns,
    });

    setNotificationData({
      preferences,
      keywordPatterns,
      userId,
      displayName,
    });
  }, [mx]);

  // Check if message should trigger a notification for SpecialMessages mode
  const shouldNotifySpecialMessage = useCallback((event: MatrixEvent): boolean => {
    if (!notificationData.userId) return false;
    
    const content = event.getContent();
    const body = content.body?.toLowerCase() || '';
    
    // Check for user mentions in m.mentions
    const mentions = content['m.mentions'];
    if (mentions?.user_ids?.includes(notificationData.userId)) {
      return true;
    }
    
    // Check for @room mention
    if (mentions?.room === true) {
      return true;
    }
    
    // Check if body contains @room
    if (body.includes('@room')) {
      return true;
    }
    
    // Check if body contains user's display name
    if (notificationData.displayName && body.includes(notificationData.displayName.toLowerCase())) {
      return true;
    }
    
    // Check if body contains user's username/localpart
    const username = getMxIdLocalPart(notificationData.userId);
    if (username && body.includes(username.toLowerCase())) {
      return true;
    }
    
    // Check if body contains any of the user's keywords
    for (const keyword of notificationData.keywordPatterns) {
      if (body.includes(keyword.toLowerCase())) {
        return true;
      }
    }
    
    return false;
  }, [notificationData]);

  // Clear all dismiss timers on unmount
  useEffect(() => {
    return () => {
      dismissTimersRef.current.forEach(timer => clearTimeout(timer));
      dismissTimersRef.current.clear();
    };
  }, []);

  // Auto-dismiss notification after 10 seconds
  const scheduleDismiss = useCallback((notificationId: string) => {
    // Clear existing timer for this notification if any
    const existingTimer = dismissTimersRef.current.get(notificationId);
    if (existingTimer) {
      clearTimeout(existingTimer);
    }
    
    // Schedule new dismiss timer
    const timer = setTimeout(() => {
      setNotifications(prev => 
        prev.map(n => n.id === notificationId ? { ...n, exiting: true } : n)
      );
      setTimeout(() => {
        setNotifications(prev => prev.filter(n => n.id !== notificationId));
        dismissTimersRef.current.delete(notificationId);
      }, 200); // Wait for exit animation
    }, 10000);
    
    dismissTimersRef.current.set(notificationId, timer);
  }, []);

  // Navigate to the new message
  const handleNotificationClick = useCallback((notification: NewMessageNotification) => (e: React.MouseEvent) => {
    e.stopPropagation();
    e.preventDefault();
    
    if (!mx) return;

    const targetRoomId = notification.roomId;
    const eventId = notification.eventId;

    // Dismiss this notification
    const timer = dismissTimersRef.current.get(notification.id);
    if (timer) {
      clearTimeout(timer);
      dismissTimersRef.current.delete(notification.id);
    }
    
    setNotifications(prev => 
      prev.map(n => n.id === notification.id ? { ...n, exiting: true } : n)
    );
    setTimeout(() => {
      setNotifications(prev => prev.filter(n => n.id !== notification.id));
    }, 200);

    // Simple navigation approach - just try all paths
    try {
      const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, targetRoomId);
      
      // Check if it's a direct message
      if (mDirects.has(targetRoomId)) {
        const path = getDirectRoomPath(roomIdOrAlias, eventId);
        console.log('Navigating to direct room:', path);
        navigate(path);
        return;
      }

      // Check if it belongs to a space
      const orphanParents = getOrphanParents(roomToParents, targetRoomId);
      if (orphanParents.length > 0) {
        const parentSpace = guessPerfectParent(mx, targetRoomId, orphanParents) ?? orphanParents[0];
        const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace);
        const path = getSpaceRoomPath(pSpaceIdOrAlias, roomIdOrAlias, eventId);
        console.log('Navigating to space room:', path);
        navigate(path);
        return;
      }

      // Default to home room
      const path = getHomeRoomPath(roomIdOrAlias, eventId);
      console.log('Navigating to home room:', path);
      navigate(path);
    } catch (error) {
      console.error('Error navigating to room:', error);
      // Last resort - navigate without event ID
      try {
        const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, targetRoomId);
        navigate(getHomeRoomPath(roomIdOrAlias));
      } catch (fallbackError) {
        console.error('Fallback navigation also failed:', fallbackError);
      }
    }
  }, [mx, roomToParents, mDirects, navigate]);

  // Subscribe to room changes for unread updates and new messages
  useEffect(() => {
    if (!mx) return;

    const update = () => setUpdateCounter(c => c + 1);

    const handleTimelineEvent = (event: MatrixEvent, room: Room | undefined) => {
      update();
      
      // Check if this is a new message we should notify about
      if (!room || !event) return;
      
      // Ignore if it's the currently active room
      if (room.roomId === roomId) {
        return;
      }
      
      // Ignore if it's our own message
      if (event.getSender() === mx.getUserId()) {
        console.log('[TitleBar] Skipping: own message');
        return;
      }
      
      // Only notify for actual messages
      if (event.getType() !== 'm.room.message') {
        console.log('[TitleBar] Skipping: not a message event, type:', event.getType());
        return;
      }
      
      // Only show for recent messages (within 15 seconds) to avoid showing notifications
      // for old messages when loading history
      const eventTime = event.getTs();
      const now = Date.now();
      if (now - eventTime > 15000) {
        console.log('[TitleBar] Skipping: message too old', (now - eventTime) / 1000, 'seconds');
        return; // More than 15 seconds old
      }
      
      // Only show for unread messages - check if event is after the last read receipt
      const readUpToEventId = room.getEventReadUpTo(mx.getUserId() ?? '');
      if (readUpToEventId) {
        const timeline = room.getLiveTimeline().getEvents();
        const readUpToIndex = timeline.findIndex(e => e.getId() === readUpToEventId);
        const eventIndex = timeline.findIndex(e => e.getId() === event.getId());
        
        // If we found both events and the new event is not after the read marker, skip it
        if (readUpToIndex !== -1 && eventIndex !== -1 && eventIndex <= readUpToIndex) {
          console.log('[TitleBar] Skipping: already read (eventIndex:', eventIndex, ', readUpToIndex:', readUpToIndex, ')');
          return;
        }
      }
      
      // Determine notification mode for this room
      const roomIdStr = room.roomId;
      let notificationMode: RoomNotificationMode;
      
      if (notificationData.preferences.mute.has(roomIdStr)) {
        notificationMode = RoomNotificationMode.Mute;
      } else if (notificationData.preferences.specialMessages.has(roomIdStr)) {
        notificationMode = RoomNotificationMode.SpecialMessages;
      } else if (notificationData.preferences.allMessages.has(roomIdStr)) {
        notificationMode = RoomNotificationMode.AllMessages;
      } else {
        notificationMode = RoomNotificationMode.Unset;
      }
      
      // Debug logging
      console.log('[TitleBar Notification]', {
        roomId: roomIdStr,
        roomName: room.name,
        notificationMode,
        isDM: mDirects.has(roomIdStr),
        preferences: {
          muted: notificationData.preferences.mute.has(roomIdStr),
          specialMessages: notificationData.preferences.specialMessages.has(roomIdStr),
          allMessages: notificationData.preferences.allMessages.has(roomIdStr),
        },
      });
      
      // Never show for muted rooms
      if (notificationMode === RoomNotificationMode.Mute) return;
      
      // DMs with Unset mode should notify for all messages (underride rule behavior)
      const isDM = mDirects.has(roomIdStr);
      const treatAsAllMessages = notificationMode === RoomNotificationMode.AllMessages || 
                                  (notificationMode === RoomNotificationMode.Unset && isDM);
      
      console.log('[TitleBar] treatAsAllMessages:', treatAsAllMessages, 'isDM:', isDM);
      
      // For SpecialMessages mode, only show if message would trigger a notification
      // For Unset mode (non-DM), also check for special messages
      if (!treatAsAllMessages && notificationMode === RoomNotificationMode.SpecialMessages) {
        const shouldNotify = shouldNotifySpecialMessage(event);
        console.log('[TitleBar] SpecialMessages mode check:', shouldNotify);
        if (!shouldNotify) return;
      }
      if (!treatAsAllMessages && notificationMode === RoomNotificationMode.Unset && !isDM) {
        const shouldNotify = shouldNotifySpecialMessage(event);
        console.log('[TitleBar] Unset mode (non-DM) check:', shouldNotify);
        if (!shouldNotify) return;
      }
      
      console.log('[TitleBar] Passed all checks, showing notification bubble');
      
      // For AllMessages mode, show all messages (no additional check needed)
      
      // Get content
      const content = event.getContent();
      if (!content.body) return;
      
      // Get sender display name
      const sender = room.getMember(event.getSender() || '');
      const senderName = sender?.name || event.getSender() || 'Unknown';
      
      // Create preview (truncate if needed)
      const preview = content.body.length > 30 
        ? content.body.substring(0, 30) + '...' 
        : content.body;
      
      // Generate unique ID for this notification
      const notificationId = `${room.roomId}-${event.getId()}-${Date.now()}`;
      
      // Add to notifications array (limit to 4 notifications)
      setNotifications(prev => {
        const newNotification: NewMessageNotification = {
          id: notificationId,
          roomId: room.roomId,
          eventId: event.getId() || '',
          senderName,
          preview,
        };
        
        // Remove oldest if we already have 4
        const updated = prev.length >= 4 ? prev.slice(1) : prev;
        return [...updated, newNotification];
      });
      
      scheduleDismiss(notificationId);
    };

    mx.on(RoomEvent.Timeline, handleTimelineEvent);
    mx.on(RoomEvent.Receipt, update);
    mx.on(ClientEvent.Room, update);
    
    return () => {
      mx.off(RoomEvent.Timeline, handleTimelineEvent);
      mx.off(RoomEvent.Receipt, update);
      mx.off(ClientEvent.Room, update);
    };
  }, [mx, roomId, scheduleDismiss, notificationData, shouldNotifySpecialMessage]);

  // Clear notifications when user reads messages in a room
  useEffect(() => {
    if (!mx) return;

    const handleReceipt = (event: MatrixEvent, room: Room) => {
      const myUserId = mx.getUserId();
      if (!myUserId) return;
      
      const content = event.getContent();
      const isMyReceipt = Object.keys(content).find((eventId) =>
        (Object.keys(content[eventId]) as any[]).find(
          (receiptType) => content[eventId][receiptType][myUserId]
        )
      );
      
      // If this is my receipt, clear all notifications for this room
      if (isMyReceipt) {
        setNotifications(prev => {
          const updated = prev.filter(n => n.roomId !== room.roomId);
          // Clear timers for removed notifications
          prev.forEach(n => {
            if (n.roomId === room.roomId) {
              const timer = dismissTimersRef.current.get(n.id);
              if (timer) {
                clearTimeout(timer);
                dismissTimersRef.current.delete(n.id);
              }
            }
          });
          return updated;
        });
      }
    };

    mx.on(RoomEvent.Receipt, handleReceipt);
    return () => {
      mx.off(RoomEvent.Receipt, handleReceipt);
    };
  }, [mx]);

  // Clear notifications for the active room when it changes
  useEffect(() => {
    if (!roomId) return;
    
    setNotifications(prev => {
      const updated = prev.filter(n => n.roomId !== roomId);
      // Clear timers for removed notifications
      prev.forEach(n => {
        if (n.roomId === roomId) {
          const timer = dismissTimersRef.current.get(n.id);
          if (timer) {
            clearTimeout(timer);
            dismissTimersRef.current.delete(n.id);
          }
        }
      });
      return updated;
    });
  }, [roomId]);

  // Build breadcrumb path for room (Space > Subspace > RoomName)
  const breadcrumbPath = useMemo(() => {
    if (!mx || !roomId) return null;
    
    const room = mx.getRoom(roomId);
    if (!room) return null;
    
    const roomName = room.name;
    if (!roomName) return null;
    
    // DMs just show the room name without hierarchy
    if (mDirects.has(roomId)) {
      return roomName;
    }
    
    // Get the parent chain
    const directParents = roomToParents.get(roomId);
    if (!directParents || directParents.size === 0) {
      // No parents, just the room name
      return roomName;
    }
    
    // Build the parent chain recursively
    // This returns the chain of parent names ABOVE the given roomId, not including roomId itself
    const buildParentChain = (currentRoomId: string): string[] => {
      const parents = roomToParents.get(currentRoomId);
      if (!parents || parents.size === 0) {
        // No parents above this room
        return [];
      }
      
      // Pick the best parent (or first if only one)
      const parentsList = Array.from(parents);
      let chosenParent: string;
      
      if (parentsList.length === 1) {
        chosenParent = parentsList[0];
      } else {
        // Use heuristic to pick best parent
        // Prefer a parent that is itself a top-level space (orphan)
        const orphanParents = parentsList.filter(p => !roomToParents.has(p));
        if (orphanParents.length > 0) {
          // Use guessPerfectParent if available, otherwise just pick first orphan
          chosenParent = guessPerfectParent(mx, currentRoomId, orphanParents) ?? orphanParents[0];
        } else {
          // All parents have parents themselves, just pick first and continue
          chosenParent = parentsList[0];
        }
      }
      
      // Get the parent's name
      const parentRoom = mx.getRoom(chosenParent);
      if (!parentRoom?.name) return [];
      
      // Get the chain above the parent
      const upperChain = buildParentChain(chosenParent);
      
      // Return the upper chain plus this parent
      return [...upperChain, parentRoom.name];
    };
    
    const parentChain = buildParentChain(roomId);
    const path = [...parentChain, roomName];
    
    return path.join(' > ');
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mx, roomId, roomToParents, mDirects, updateCounter]);

  // Get room name (for backward compatibility, keeping simple name too)
  const roomName = useMemo(() => {
    if (!mx || !roomId) return null;
    const room = mx.getRoom(roomId);
    return room?.name || null;
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mx, roomId, updateCounter]);

  // Get total unread count
  const totalUnread = useMemo(() => {
    if (!mx) return 0;
    const unreadInfos = getUnreadInfos(mx);
    return unreadInfos.reduce((sum, info) => sum + info.total, 0);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mx, updateCounter]);

  // Build title parts
  const titleParts = useMemo(() => {
    let suffix = '';
    if (breadcrumbPath) {
      suffix += ` | ${breadcrumbPath}`;
    }
    if (totalUnread > 0) {
      suffix += ` (${totalUnread})`;
    }
    return { suffix };
  }, [breadcrumbPath, totalUnread]);

  const handleDragStart = async () => {
    try {
      await invoke('window_start_drag');
    } catch (error) {
      console.error('Failed to start window drag:', error);
    }
  };

  // Only render on desktop (when Tauri is available)
  if (typeof (window as any).__TAURI__ === 'undefined') {
    return null;
  }

  // Determine sync status badge
  let syncStatusBadge: ReactNode = null;
  if (mx) {
    if (
      (syncStateData.current === SyncState.Prepared ||
        syncStateData.current === SyncState.Syncing ||
        syncStateData.current === SyncState.Catchup) &&
      syncStateData.previous !== SyncState.Syncing
    ) {
      syncStatusBadge = (
        <div className={`${css.SyncStatusBadge} ${css.SyncStatusSuccess}`}>
          Connecting...
        </div>
      );
    } else if (syncStateData.current === SyncState.Reconnecting) {
      syncStatusBadge = (
        <div className={`${css.SyncStatusBadge} ${css.SyncStatusWarning}`}>
          Reconnecting...
        </div>
      );
    } else if (syncStateData.current === SyncState.Error) {
      syncStatusBadge = (
        <div className={`${css.SyncStatusBadge} ${css.SyncStatusCritical}`}>
          Connection Lost
        </div>
      );
    }
  }

  return (
    <Box className={css.TitleBar} alignItems="Center" justifyContent="SpaceBetween">
      {syncStatusBadge}
      <Box 
        className={css.TitleBarDragRegion}
        alignItems="Center"
        gap="200"
        grow="Yes"
        onMouseDown={handleDragStart}
      >
        {showInTitleBar && (
          <IconButton
            className={css.TitleBarBackButton}
            size="300"
            variant="Surface"
            fill="None"
            radii="300"
            onClick={goBack}
            onMouseDown={(e) => e.stopPropagation()}
            aria-label="Back"
          >
            <Icon src={Icons.ArrowLeft} size="200" />
          </IconButton>
        )}
        <Text size="T200" truncate style={{ color: 'inherit' }}>
          <span style={{ fontWeight: 700 }}>Paarrot</span>{titleParts.suffix}
        </Text>
        {notifications.map((notification) => (
          <div 
            key={notification.id}
            className={`${css.NewMessagePill} ${notification.exiting ? css.NewMessagePillExiting : ''}`}
            onClick={handleNotificationClick(notification)}
            onMouseDown={(e) => e.stopPropagation()}
            title={`${notification.senderName}: ${notification.preview}`}
          >
            <span>{notification.senderName}: {notification.preview}</span>
          </div>
        ))}
        {children}
      </Box>
      <Box alignItems="Center" gap="100" style={{ height: '100%', flexShrink: 0 }}>
        <UpdateNotification />
        <WindowControls />
      </Box>
    </Box>
  );
}
