'use client';

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useEffect, useState, useCallback, useRef } from 'react';
import { monitoringApi } from '@/lib/api';
import type { LogEntry, LogFilters, LogsResponse, StatsResponse, SystemStats } from '@/types/monitoring';

// Query keys
export const monitoringKeys = {
  all: ['monitoring'] as const,
  logs: (filters?: LogFilters) => [...monitoringKeys.all, 'logs', filters] as const,
  stats: () => [...monitoringKeys.all, 'stats'] as const,
};

// Hook for fetching logs
export function useLogs(filters?: LogFilters) {
  return useQuery({
    queryKey: monitoringKeys.logs(filters),
    queryFn: () => monitoringApi.getLogs(filters),
    refetchInterval: 10000, // Refetch every 10 seconds
  });
}

// Hook for fetching stats
export function useStats() {
  return useQuery({
    queryKey: monitoringKeys.stats(),
    queryFn: () => monitoringApi.getStats(),
    refetchInterval: 5000, // Refetch every 5 seconds
  });
}

// Hook for clearing logs
export function useClearLogs() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: () => monitoringApi.clearLogs(),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: monitoringKeys.logs() });
    },
  });
}

// WebSocket connection status
export type WSStatus = 'connecting' | 'connected' | 'disconnected' | 'error';

// Hook for real-time log streaming via WebSocket
export function useLogStream(enabled: boolean = true) {
  const [logs, setLogs] = useState<LogEntry[]>([]);
  const [status, setStatus] = useState<WSStatus>('disconnected');
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const enabledRef = useRef(enabled);
  const maxLogs = 500; // Keep at most 500 logs in memory

  // Keep enabledRef in sync
  useEffect(() => {
    enabledRef.current = enabled;
  }, [enabled]);

  const disconnect = useCallback(() => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }
    setStatus('disconnected');
  }, []);

  const connect = useCallback(() => {
    // Only run in browser
    if (typeof window === 'undefined') return;
    if (!enabledRef.current) return;

    // Clean up existing connection
    if (wsRef.current) {
      wsRef.current.close();
    }

    setStatus('connecting');

    try {
      const wsUrl = monitoringApi.getWebSocketUrl();
      const ws = new WebSocket(wsUrl);

      ws.onopen = () => {
        setStatus('connected');
        // Clear reconnect timeout
        if (reconnectTimeoutRef.current) {
          clearTimeout(reconnectTimeoutRef.current);
          reconnectTimeoutRef.current = null;
        }
      };

      ws.onmessage = (event) => {
        try {
          const message = JSON.parse(event.data);
          if (message.type === 'log' && message.data) {
            const newLog = message.data as LogEntry;
            setLogs((prev) => {
              // Add new log at the beginning, limit total logs
              const updated = [newLog, ...prev].slice(0, maxLogs);
              return updated;
            });
          }
        } catch (err) {
          console.error('Failed to parse WebSocket message:', err);
        }
      };

      ws.onerror = () => {
        setStatus('error');
      };

      ws.onclose = () => {
        setStatus('disconnected');
        wsRef.current = null;

        // Attempt to reconnect after 3 seconds
        if (enabledRef.current && !reconnectTimeoutRef.current) {
          reconnectTimeoutRef.current = setTimeout(() => {
            reconnectTimeoutRef.current = null;
            connect();
          }, 3000);
        }
      };

      wsRef.current = ws;
    } catch (err) {
      console.error('Failed to create WebSocket:', err);
      setStatus('error');
    }
  }, []);

  const clearLogs = useCallback(() => {
    setLogs([]);
  }, []);

  useEffect(() => {
    if (enabled) {
      connect();
    } else {
      disconnect();
    }

    return () => {
      disconnect();
    };
  }, [enabled, connect, disconnect]);

  return {
    logs,
    status,
    connect,
    disconnect,
    clearLogs,
  };
}

// Combined hook for monitoring page
export function useMonitoring(filters?: LogFilters) {
  const { data: logsData, isLoading: logsLoading, error: logsError, refetch: refetchLogs } = useLogs(filters);
  const { data: statsData, isLoading: statsLoading, error: statsError, refetch: refetchStats } = useStats();
  const clearLogsMutation = useClearLogs();

  return {
    logs: logsData,
    stats: statsData,
    isLoading: logsLoading || statsLoading,
    logsError,
    statsError,
    refetchLogs,
    refetchStats,
    clearLogs: clearLogsMutation.mutate,
    isClearingLogs: clearLogsMutation.isPending,
  };
}

// Utility to format bytes to human-readable
export function formatBytes(bytes: number | undefined | null): string {
  if (bytes == null || bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}

// Utility to format uptime
export function formatUptime(seconds: number | undefined | null): string {
  if (seconds == null) return '0s';
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);

  const parts = [];
  if (days > 0) parts.push(`${days}d`);
  if (hours > 0) parts.push(`${hours}h`);
  if (minutes > 0) parts.push(`${minutes}m`);
  if (parts.length === 0) parts.push(`${Math.floor(seconds)}s`);

  return parts.join(' ');
}

// Utility to format percentage
export function formatPercent(value: number | undefined | null): string {
  if (value == null) return '0%';
  return `${value.toFixed(1)}%`;
}

// Utility to format latency
export function formatLatency(ms: number | undefined | null): string {
  if (ms == null) return '0ms';
  if (ms < 1) {
    return `${(ms * 1000).toFixed(0)}µs`;
  }
  if (ms < 1000) {
    return `${ms.toFixed(1)}ms`;
  }
  return `${(ms / 1000).toFixed(2)}s`;
}

// Safe toFixed helper
export function safeToFixed(value: number | undefined | null, digits: number = 2): string {
  if (value == null) return '0';
  return value.toFixed(digits);
}
