/**
 * useNotifications Hook
 * 
 * Hook for managing in-app notifications.
 * Supports fetching, marking as read, and executing actions.
 */

import { useState, useCallback, useEffect, useRef } from 'react';
import api from '../api';

const _ACCESS_REF_TYPES = new Set(['mandate_access', 'feature_access']);

/** API uses PowerOnModel.sysCreatedAt (seconds); legacy clients used createdAt. */
function _coerceToUnixSeconds(value: unknown): number | undefined {
  if (value == null) return undefined;
  if (typeof value === 'number' && Number.isFinite(value)) {
    return value > 1e12 ? value / 1000 : value;
  }
  if (typeof value === 'string') {
    const trimmed = value.trim();
    if (!trimmed) return undefined;
    const asNum = Number(trimmed);
    if (!Number.isNaN(asNum)) {
      return asNum > 1e12 ? asNum / 1000 : asNum;
    }
    const parsed = Date.parse(trimmed);
    if (!Number.isNaN(parsed)) return parsed / 1000;
  }
  return undefined;
}

function _normalizeNotificationFromApi(raw: Record<string, unknown>): UserNotification {
  const partial = raw as unknown as UserNotification;
  const createdAt =
    _coerceToUnixSeconds(raw.createdAt) ??
    _coerceToUnixSeconds(raw.sysCreatedAt) ??
    (Number.isFinite(partial.createdAt) ? partial.createdAt : 0) ??
    0;
  return {
    ...partial,
    createdAt,
    readAt: _coerceToUnixSeconds(raw.readAt) ?? partial.readAt,
    actionedAt: _coerceToUnixSeconds(raw.actionedAt) ?? partial.actionedAt,
    expiresAt: _coerceToUnixSeconds(raw.expiresAt) ?? partial.expiresAt,
  };
}

function _normalizeNotificationList(data: unknown): UserNotification[] {
  if (!Array.isArray(data)) return [];
  return data.map(item =>
    _normalizeNotificationFromApi(item && typeof item === 'object' ? (item as Record<string, unknown>) : {})
  );
}

// Types
export interface NotificationAction {
  actionId: string;
  label: string;
  style: 'primary' | 'danger' | 'default';
}

export interface UserNotification {
  id: string;
  userId: string;
  type: 'invitation' | 'system' | 'workflow' | 'mention';
  status: 'unread' | 'read' | 'actioned' | 'dismissed';
  title: string;
  message: string;
  icon?: string;
  referenceType?: string;
  referenceId?: string;
  actions?: NotificationAction[];
  actionTaken?: string;
  actionResult?: string;
  /** Creation time as Unix seconds (UTC); filled from API sysCreatedAt when needed */
  createdAt: number;
  readAt?: number;
  actionedAt?: number;
  expiresAt?: number;
}

export interface NotificationActionResult {
  message: string;
  action: string;
  notificationId: string;
}

/**
 * Hook for managing notifications
 */
export function useNotifications() {
  const [notifications, setNotifications] = useState<UserNotification[]>([]);
  const [unreadCount, setUnreadCount] = useState<number>(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  
  // Polling interval ref
  const pollingIntervalRef = useRef<NodeJS.Timeout | null>(null);
  const prevUnreadCountRef = useRef<number | null>(null);

  /**
   * Fetch all notifications for the current user
   */
  const fetchNotifications = useCallback(async (
    options?: { status?: string; type?: string; limit?: number }
  ): Promise<UserNotification[]> => {
    setLoading(true);
    setError(null);
    
    try {
      const params = new URLSearchParams();
      if (options?.status) params.append('status', options.status);
      if (options?.type) params.append('type', options.type);
      if (options?.limit) params.append('limit', options.limit.toString());
      
      const queryString = params.toString();
      const url = `/api/notifications${queryString ? `?${queryString}` : ''}`;
      
      const response = await api.get(url);
      const data = _normalizeNotificationList(response.data);
      setNotifications(data);
      return data;
    } catch (err: any) {
      const errorMessage = err.response?.data?.detail || 'Fehler beim Laden der Benachrichtigungen';
      setError(errorMessage);
      return [];
    } finally {
      setLoading(false);
    }
  }, []);

  /**
   * Fetch unread count
   */
  const fetchUnreadCount = useCallback(async (): Promise<number> => {
    try {
      const response = await api.get('/api/notifications/unread-count');
      const count = response.data.count;
      const prev = prevUnreadCountRef.current;
      prevUnreadCountRef.current = count;

      if (prev !== null && count > prev) {
        try {
          const listRes = await api.get('/api/notifications', {
            params: { status: 'unread', limit: 25 },
          });
          const list = _normalizeNotificationList(listRes.data);
          if (
            list.length > 0 &&
            list.some(n => n.referenceType && _ACCESS_REF_TYPES.has(n.referenceType))
          ) {
            window.dispatchEvent(new Event('features-changed'));
          }
        } catch {
          /* ignore */
        }
      }

      setUnreadCount(count);
      return count;
    } catch (err: any) {
      console.error('Failed to fetch unread count:', err);
      return 0;
    }
  }, []);

  /**
   * Mark a notification as read
   */
  const markAsRead = useCallback(async (notificationId: string): Promise<boolean> => {
    try {
      await api.put(`/api/notifications/${notificationId}/read`);
      
      // Update local state
      setNotifications(prev => 
        prev.map(n => 
          n.id === notificationId 
            ? { ...n, status: 'read' as const, readAt: Date.now() / 1000 }
            : n
        )
      );
      
      // Update unread count
      setUnreadCount(prev => Math.max(0, prev - 1));
      
      return true;
    } catch (err: any) {
      console.error('Failed to mark notification as read:', err);
      return false;
    }
  }, []);

  /**
   * Mark all notifications as read
   */
  const markAllAsRead = useCallback(async (): Promise<boolean> => {
    try {
      await api.put('/api/notifications/mark-all-read');
      
      // Update local state
      setNotifications(prev => 
        prev.map(n => 
          n.status === 'unread' 
            ? { ...n, status: 'read' as const, readAt: Date.now() / 1000 }
            : n
        )
      );
      
      setUnreadCount(0);
      
      return true;
    } catch (err: any) {
      console.error('Failed to mark all notifications as read:', err);
      return false;
    }
  }, []);

  /**
   * Execute an action on a notification
   */
  const executeAction = useCallback(async (
    notificationId: string, 
    actionId: string
  ): Promise<NotificationActionResult | null> => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await api.post(`/api/notifications/${notificationId}/action`, {
        actionId
      });
      
      const result = response.data as NotificationActionResult;
      
      // Update local state
      setNotifications(prev => 
        prev.map(n => 
          n.id === notificationId 
            ? { 
                ...n, 
                status: 'actioned' as const, 
                actionTaken: actionId,
                actionResult: result.message,
                actionedAt: Date.now() / 1000 
              }
            : n
        )
      );
      
      // Update unread count if it was unread
      const notification = notifications.find(n => n.id === notificationId);
      if (notification?.status === 'unread') {
        setUnreadCount(prev => Math.max(0, prev - 1));
      }
      
      return result;
    } catch (err: any) {
      const errorMessage = err.response?.data?.detail || 'Fehler bei der Ausführung der Aktion';
      setError(errorMessage);
      return null;
    } finally {
      setLoading(false);
    }
  }, [notifications]);

  /**
   * Dismiss/delete a notification
   */
  const dismissNotification = useCallback(async (notificationId: string): Promise<boolean> => {
    try {
      await api.delete(`/api/notifications/${notificationId}`);
      
      // Update local state
      const notification = notifications.find(n => n.id === notificationId);
      
      setNotifications(prev => prev.filter(n => n.id !== notificationId));
      
      // Update unread count if it was unread
      if (notification?.status === 'unread') {
        setUnreadCount(prev => Math.max(0, prev - 1));
      }
      
      return true;
    } catch (err: any) {
      console.error('Failed to dismiss notification:', err);
      return false;
    }
  }, [notifications]);

  /**
   * Start polling for new notifications
   */
  const startPolling = useCallback((intervalMs: number = 30000) => {
    // Clear any existing interval
    if (pollingIntervalRef.current) {
      clearInterval(pollingIntervalRef.current);
    }
    
    // Fetch immediately
    fetchUnreadCount();
    
    // Set up polling
    pollingIntervalRef.current = setInterval(() => {
      fetchUnreadCount();
    }, intervalMs);
  }, [fetchUnreadCount]);

  /**
   * Stop polling
   */
  const stopPolling = useCallback(() => {
    if (pollingIntervalRef.current) {
      clearInterval(pollingIntervalRef.current);
      pollingIntervalRef.current = null;
    }
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (pollingIntervalRef.current) {
        clearInterval(pollingIntervalRef.current);
      }
    };
  }, []);

  return {
    notifications,
    unreadCount,
    loading,
    error,
    fetchNotifications,
    fetchUnreadCount,
    markAsRead,
    markAllAsRead,
    executeAction,
    dismissNotification,
    startPolling,
    stopPolling
  };
}

export default useNotifications;
