/**
 * Enhanced Control Panel Data Hook
 * 
 * Handles initial API fetch, SSE integration, and data merging with priority rules
 * for the control panel page.
 */

import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useState, useCallback, useRef } from 'react';
import { alertService } from '../services/alertService';
import { getPendingApprovalPurchaseOrders } from '../services/purchase_orders';
import { productionService } from '../services/production';
import { ProcurementService } from '../services/procurement-service';
import * as orchestratorService from '../services/orchestrator';
import { suppliersService } from '../services/suppliers';
import { aiInsightsService } from '../services/aiInsights';
import { useSSEEvents } from '../../hooks/useSSE';
import { parseISO } from 'date-fns';

// Debounce delay for SSE-triggered query invalidations (ms)
const SSE_INVALIDATION_DEBOUNCE_MS = 500;

// Delay before SSE invalidations are allowed after initial load (ms)
// This prevents duplicate API calls when SSE events arrive during/right after initial fetch
const SSE_INITIAL_LOAD_GRACE_PERIOD_MS = 3000;

// ============================================================
// Types
// ============================================================

export interface ControlPanelData {
  // Raw data from APIs
  alerts: any[];
  pendingPOs: any[];
  productionBatches: any[];
  deliveries: any[];
  orchestrationSummary: OrchestrationSummary | null;
  aiInsights: any[];
  
  // Computed/derived data
  preventedIssues: any[];
  issuesRequiringAction: number;
  issuesPreventedByAI: number;
  
  // Filtered data for blocks
  overdueDeliveries: any[];
  pendingDeliveries: any[];
  lateToStartBatches: any[];
  runningBatches: any[];
  pendingBatches: any[];
  
  // Categorized alerts
  equipmentAlerts: any[];
  productionAlerts: any[];
  otherAlerts: any[];
}

export interface OrchestrationSummary {
  runTimestamp: string | null;
  runNumber?: number;
  status: string;
  purchaseOrdersCreated: number;
  productionBatchesCreated: number;
  userActionsRequired: number;
  aiHandlingRate?: number;
  estimatedSavingsEur?: number;
}

// ============================================================
// Data Priority and Merging Logic
// ============================================================

/**
 * Merge data with priority rules:
 * 1. Services API data takes precedence
 * 2. Alerts data enriches services data
 * 3. Alerts data is used as fallback when no services data exists
 * 4. Deduplicate alerts for entities already shown in UI
 */
function mergeDataWithPriority(
  servicesData: any,
  alertsData: any,
  entityType: 'po' | 'batch' | 'delivery'
): any[] {
  const mergedEntities = [...servicesData];
  const servicesEntityIds = new Set(servicesData.map((entity: any) => entity.id));
  
  // Enrich services data with alerts data
  const enrichedEntities = mergedEntities.map(entity => {
    const matchingAlert = alertsData.find((alert: any) => 
      alert.entity_links?.[entityType === 'po' ? 'purchase_order' : entityType === 'batch' ? 'production_batch' : 'delivery'] === entity.id
    );
    
    if (matchingAlert) {
      return {
        ...entity,
        alert_reasoning: matchingAlert.reasoning_data,
        alert_priority: matchingAlert.priority_level,
        alert_timestamp: matchingAlert.timestamp,
      };
    }
    
    return entity;
  });
  
  // Add alerts data as fallback for entities not in services data
  alertsData.forEach((alert: any) => {
    const entityId = alert.entity_links?.[entityType === 'po' ? 'purchase_order' : entityType === 'batch' ? 'production_batch' : 'delivery'];
    
    if (entityId && !servicesEntityIds.has(entityId)) {
      // Create a synthetic entity from alert data
      const syntheticEntity = {
        id: entityId,
        status: alert.event_metadata?.status || 'UNKNOWN',
        alert_reasoning: alert.reasoning_data,
        alert_priority: alert.priority_level,
        alert_timestamp: alert.timestamp,
        source: 'alert_fallback',
      };
      
      // Add entity-specific fields from alert metadata
      if (entityType === 'po') {
        (syntheticEntity as any).supplier_id = alert.event_metadata?.supplier_id;
        (syntheticEntity as any).po_number = alert.event_metadata?.po_number;
      } else if (entityType === 'batch') {
        (syntheticEntity as any).batch_number = alert.event_metadata?.batch_number;
        (syntheticEntity as any).product_id = alert.event_metadata?.product_id;
      } else if (entityType === 'delivery') {
        (syntheticEntity as any).expected_delivery_date = alert.event_metadata?.expected_delivery_date;
      }
      
      enrichedEntities.push(syntheticEntity);
    }
  });
  
  return enrichedEntities;
}

/**
 * Categorize alerts by type
 */
function categorizeAlerts(alerts: any[], batchIds: Set<string>, deliveryIds: Set<string>): {
  equipmentAlerts: any[],
  productionAlerts: any[],
  otherAlerts: any[]
} {
  const equipmentAlerts: any[] = [];
  const productionAlerts: any[] = [];
  const otherAlerts: any[] = [];
  
  alerts.forEach(alert => {
    const eventType = alert.event_type || '';
    const batchId = alert.event_metadata?.batch_id || alert.entity_links?.production_batch;
    const deliveryId = alert.event_metadata?.delivery_id || alert.entity_links?.delivery;
    
    // Equipment alerts
    if (eventType.includes('equipment_') || 
        eventType.includes('maintenance') ||
        eventType.includes('machine_failure')) {
      equipmentAlerts.push(alert);
    }
    // Production alerts (not equipment-related)
    else if (eventType.includes('production.') || 
             eventType.includes('batch_') || 
             eventType.includes('production_') ||
             eventType.includes('delay') ||
             (batchId && !batchIds.has(batchId))) {
      productionAlerts.push(alert);
    }
    // Other alerts
    else {
      otherAlerts.push(alert);
    }
  });
  
  return { equipmentAlerts, productionAlerts, otherAlerts };
}

// ============================================================
// Main Hook
// ============================================================

export function useControlPanelData(tenantId: string) {
  const queryClient = useQueryClient();
  const [sseEvents, setSseEvents] = useState<any[]>([]);
  
  // Subscribe to SSE events for control panel
  const { events: sseAlerts } = useSSEEvents({ 
    channels: ['*.alerts', '*.notifications', 'recommendations'] 
  });
  
  // Update SSE events state when new events arrive
  useEffect(() => {
    if (sseAlerts.length > 0) {
      setSseEvents(prev => {
        // Deduplicate by event ID
        const eventIds = new Set(prev.map(e => e.id));
        const newEvents = sseAlerts.filter(event => !eventIds.has(event.id));
        return [...prev, ...newEvents];
      });
    }
  }, [sseAlerts]);
  
  const query = useQuery<ControlPanelData>({
    queryKey: ['control-panel-data', tenantId],
    queryFn: async () => {
      const today = new Date().toISOString().split('T')[0];
      const now = new Date();
      const nowUTC = new Date();

      // Parallel fetch from all services
      const [alertsResponse, pendingPOs, productionResponse, deliveriesResponse, orchestration, suppliers, aiInsightsResponse] = await Promise.all([
        alertService.getEvents(tenantId, { status: 'active', limit: 100 }).catch(() => []),
        getPendingApprovalPurchaseOrders(tenantId, 100).catch(() => []),
        productionService.getBatches(tenantId, { start_date: today, page_size: 100 }).catch(() => ({ batches: [] })),
        ProcurementService.getExpectedDeliveries(tenantId, { days_ahead: 1, include_overdue: true }).catch(() => ({ deliveries: [] })),
        orchestratorService.getLastOrchestrationRun(tenantId).catch(() => null),
        suppliersService.getSuppliers(tenantId).catch(() => []),
        aiInsightsService.getInsights(tenantId, {
          status: 'new',
          priority: 'high',
          limit: 5
        }).catch(() => ({ items: [], total: 0, limit: 5, offset: 0, has_more: false })),
      ]);

      // Normalize responses
      const alerts = Array.isArray(alertsResponse) ? alertsResponse : (alertsResponse?.items || []);
      const productionBatches = productionResponse?.batches || [];
      const deliveries = deliveriesResponse?.deliveries || [];
      const aiInsights = aiInsightsResponse?.items || [];

      // Create supplier map
      const supplierMap = new Map<string, string>();
      (suppliers || []).forEach((supplier: any) => {
        supplierMap.set(supplier.id, supplier.name || supplier.supplier_name);
      });

      // Merge SSE events with API data (deduplicate by ID, prioritizing SSE events as they're newer)
      let allAlerts: any[];
      if (sseEvents.length > 0) {
        const sseEventIds = new Set(sseEvents.map(e => e.id));
        // Filter out API alerts that also exist in SSE (SSE has newer data)
        const uniqueApiAlerts = alerts.filter((alert: any) => !sseEventIds.has(alert.id));
        allAlerts = [...uniqueApiAlerts, ...sseEvents];
      } else {
        allAlerts = [...alerts];
      }

      // Apply data priority rules for POs
      const enrichedPendingPOs = mergeDataWithPriority(pendingPOs, allAlerts, 'po');
      
      // Apply data priority rules for batches
      const enrichedProductionBatches = mergeDataWithPriority(productionBatches, allAlerts, 'batch');
      
      // Apply data priority rules for deliveries
      const enrichedDeliveries = mergeDataWithPriority(deliveries, allAlerts, 'delivery');

      // Filter and categorize data
      const isPending = (status: string) =>
        status === 'PENDING' || status === 'sent_to_supplier' || status === 'confirmed';

      const overdueDeliveries = enrichedDeliveries.filter((d: any) => {
        if (!isPending(d.status)) return false;
        const expectedDate = parseISO(d.expected_delivery_date);
        return expectedDate < nowUTC;
      }).map((d: any) => ({
        ...d,
        hoursOverdue: Math.ceil((nowUTC.getTime() - parseISO(d.expected_delivery_date).getTime()) / (1000 * 60 * 60)),
      }));

      const pendingDeliveriesFiltered = enrichedDeliveries.filter((d: any) => {
        if (!isPending(d.status)) return false;
        const expectedDate = parseISO(d.expected_delivery_date);
        return expectedDate >= nowUTC;
      }).map((d: any) => ({
        ...d,
        hoursUntil: Math.ceil((parseISO(d.expected_delivery_date).getTime() - nowUTC.getTime()) / (1000 * 60 * 60)),
      }));

      // Filter production batches
      const lateToStartBatches = enrichedProductionBatches.filter((b: any) => {
        const status = b.status?.toUpperCase();
        if (status !== 'PENDING' && status !== 'SCHEDULED') return false;
        const plannedStart = b.planned_start_time;
        if (!plannedStart) return false;
        return parseISO(plannedStart) < nowUTC;
      }).map((b: any) => ({
        ...b,
        hoursLate: Math.ceil((nowUTC.getTime() - parseISO(b.planned_start_time).getTime()) / (1000 * 60 * 60)),
      }));

      const runningBatches = enrichedProductionBatches.filter((b: any) =>
        b.status?.toUpperCase() === 'IN_PROGRESS'
      );

      const pendingBatchesFiltered = enrichedProductionBatches.filter((b: any) => {
        const status = b.status?.toUpperCase();
        if (status !== 'PENDING' && status !== 'SCHEDULED') return false;
        const plannedStart = b.planned_start_time;
        if (!plannedStart) return true;
        return parseISO(plannedStart) >= nowUTC;
      });

      // Create sets for deduplication
      const lateBatchIds = new Set(lateToStartBatches.map((b: any) => b.id));
      const runningBatchIds = new Set(runningBatches.map((b: any) => b.id));
      const deliveryIds = new Set([...overdueDeliveries, ...pendingDeliveriesFiltered].map((d: any) => d.id));

      // Create array of all batch IDs for categorization
      const allBatchIds = new Set([
        ...Array.from(lateBatchIds),
        ...Array.from(runningBatchIds),
        ...pendingBatchesFiltered.map((b: any) => b.id)
      ]);

      // Categorize alerts and filter out duplicates for batches already shown
      const { equipmentAlerts, productionAlerts, otherAlerts } = categorizeAlerts(
        allAlerts,
        allBatchIds,
        deliveryIds
      );
      
      // Additional deduplication: filter out equipment alerts for batches already shown in UI
      const deduplicatedEquipmentAlerts = equipmentAlerts.filter(alert => {
        const batchId = alert.event_metadata?.batch_id || alert.entity_links?.production_batch;
        if (batchId && allBatchIds.has(batchId)) {
          return false; // Filter out if batch is already shown
        }
        return true;
      });

      // Compute derived data
      const preventedIssues = allAlerts.filter((a: any) => a.type_class === 'prevented_issue');
      const actionNeededAlerts = allAlerts.filter((a: any) =>
        a.type_class === 'action_needed' &&
        !a.hidden_from_ui &&
        a.status === 'active'
      );

      // Debug: Log alert counts by type_class
      console.log('📊 [useControlPanelData] Alert analysis:', {
        totalAlerts: allAlerts.length,
        fromAPI: alerts.length,
        fromSSE: sseEvents.length,
        preventedIssuesCount: preventedIssues.length,
        actionNeededCount: actionNeededAlerts.length,
        typeClassBreakdown: allAlerts.reduce((acc: Record<string, number>, a: any) => {
          const typeClass = a.type_class || 'unknown';
          acc[typeClass] = (acc[typeClass] || 0) + 1;
          return acc;
        }, {}),
        apiAlertsSample: alerts.slice(0, 3).map((a: any) => ({
          id: a.id,
          event_type: a.event_type,
          type_class: a.type_class,
          status: a.status,
        })),
        sseEventsSample: sseEvents.slice(0, 3).map((a: any) => ({
          id: a.id,
          event_type: a.event_type,
          type_class: a.type_class,
          status: a.status,
        })),
      });
      
      // Calculate total issues requiring action:
      // 1. Action needed alerts
      // 2. Pending PO approvals (each PO requires approval action)
      // 3. Late to start batches (each requires start action)
      const issuesRequiringAction = actionNeededAlerts.length +
                                   enrichedPendingPOs.length +
                                   lateToStartBatches.length;

      // Build orchestration summary
      let orchestrationSummary: OrchestrationSummary | null = null;
      if (orchestration && orchestration.timestamp) {
        orchestrationSummary = {
          runTimestamp: orchestration.timestamp,
          runNumber: orchestration.runNumber ?? undefined,
          status: 'completed',
          purchaseOrdersCreated: enrichedPendingPOs.length,
          productionBatchesCreated: enrichedProductionBatches.length,
          userActionsRequired: actionNeededAlerts.length,
          aiHandlingRate: preventedIssues.length > 0
            ? Math.round((preventedIssues.length / (preventedIssues.length + actionNeededAlerts.length)) * 100)
            : undefined,
          estimatedSavingsEur: preventedIssues.length * 50,
        };
      }

      return {
        // Raw data
        alerts: allAlerts,
        pendingPOs: enrichedPendingPOs,
        productionBatches: enrichedProductionBatches,
        deliveries: enrichedDeliveries,
        orchestrationSummary,
        aiInsights,
        
        // Computed
        preventedIssues,
        issuesRequiringAction,
        issuesPreventedByAI: preventedIssues.length,
        
        // Filtered for blocks
        overdueDeliveries,
        pendingDeliveries: pendingDeliveriesFiltered,
        lateToStartBatches,
        runningBatches,
        pendingBatches: pendingBatchesFiltered,
        
        // Categorized alerts (deduplicated to prevent showing alerts for batches already in UI)
        equipmentAlerts: deduplicatedEquipmentAlerts,
        productionAlerts,
        otherAlerts,
      };
    },
    enabled: !!tenantId,
    staleTime: 20000, // 20 seconds
    refetchOnMount: true,
    refetchOnWindowFocus: false,
    retry: 2,
  });

  // Ref for debouncing SSE-triggered invalidations
  const invalidationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const lastEventCountRef = useRef<number>(0);
  // Track when the initial data was successfully fetched to avoid immediate SSE refetches
  const initialLoadTimestampRef = useRef<number | null>(null);

  // Update initial load timestamp when query succeeds
  useEffect(() => {
    if (query.isSuccess && !initialLoadTimestampRef.current) {
      initialLoadTimestampRef.current = Date.now();
    }
  }, [query.isSuccess]);

  // SSE integration - invalidate query on relevant events (debounced)
  useEffect(() => {
    // Skip if no new events since last check
    if (sseAlerts.length === 0 || !tenantId || sseAlerts.length === lastEventCountRef.current) {
      return;
    }

    // OPTIMIZATION: Skip SSE-triggered invalidation during grace period after initial load
    // This prevents duplicate API calls when SSE events arrive during/right after the initial fetch
    if (initialLoadTimestampRef.current) {
      const timeSinceInitialLoad = Date.now() - initialLoadTimestampRef.current;
      if (timeSinceInitialLoad < SSE_INITIAL_LOAD_GRACE_PERIOD_MS) {
        // Update the event count ref so we don't process these events later
        lastEventCountRef.current = sseAlerts.length;
        return;
      }
    }

    const relevantEvents = sseAlerts.filter(event =>
      event.event_type?.includes('production.') ||
      event.event_type?.includes('batch_') ||
      event.event_type?.includes('delivery') ||
      event.event_type?.includes('purchase_order') ||
      event.event_type?.includes('equipment_') ||
      event.event_type?.includes('insight') ||
      event.event_type?.includes('recommendation') ||
      event.event_type?.includes('ai_') ||  // Match ai_yield_prediction, ai_*, etc.
      event.event_class === 'recommendation'
    );

    if (relevantEvents.length > 0) {
      // Clear existing timeout to debounce rapid events
      if (invalidationTimeoutRef.current) {
        clearTimeout(invalidationTimeoutRef.current);
      }

      // Debounce the invalidation to prevent multiple rapid refetches
      invalidationTimeoutRef.current = setTimeout(() => {
        lastEventCountRef.current = sseAlerts.length;
        queryClient.invalidateQueries({
          queryKey: ['control-panel-data', tenantId],
          refetchType: 'active',
        });
      }, SSE_INVALIDATION_DEBOUNCE_MS);
    }

    // Cleanup timeout on unmount or dependency change
    return () => {
      if (invalidationTimeoutRef.current) {
        clearTimeout(invalidationTimeoutRef.current);
      }
    };
  }, [sseAlerts, tenantId, queryClient]);

  return query;
}

// ============================================================
// Real-time SSE Hook for Control Panel
// ============================================================

export function useControlPanelRealtimeSync(tenantId: string) {
  const queryClient = useQueryClient();
  
  // Subscribe to SSE events
  const { events: sseEvents } = useSSEEvents({ 
    channels: ['*.alerts', '*.notifications', 'recommendations'] 
  });
  
  // Invalidate control panel data on relevant events
  useEffect(() => {
    if (sseEvents.length === 0 || !tenantId) return;

    const latest = sseEvents[0];
    const relevantEventTypes = [
      'batch_completed', 'batch_started', 'batch_state_changed',
      'delivery_received', 'delivery_overdue', 'delivery_arriving_soon',
      'stock_receipt_incomplete', 'orchestration_run_completed',
      'production_delay', 'batch_start_delayed', 'equipment_maintenance'
    ];
    
    if (relevantEventTypes.includes(latest.event_type)) {
      queryClient.invalidateQueries({
        queryKey: ['control-panel-data', tenantId],
        refetchType: 'active',
      });
    }
  }, [sseEvents, tenantId, queryClient]);
}