import { createContext, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { triggerStandardizeBatch, type StandardizeMaterialInput } from '@/services/automationService';
import { fetchStandardizationTasks, type StandardizationTaskRecord } from '@/services/standardizedService';
import {
  buildOnlineStandardizationQueueFingerprint,
  getStoredOnlineStandardizationQueue,
  saveStoredOnlineStandardizationQueue,
  type OnlineStandardizationQueueItem,
  type OnlineStandardizationQueuePayload,
} from '@/lib/standardizationQueueStorage';
import { isSupabaseConfigured } from '@/lib/supabase';
import {
  getManualInterventionAlertSoundOption,
  MANUAL_INTERVENTION_ALERT_SOUND_STORAGE_KEY,
  normalizeManualInterventionAlertSoundId,
  playAlertSound,
} from '@/lib/manualInterventionAlertSounds';

const STANDARDIZATION_MONITOR_QUERY_KEY = ['standardization', 'monitor', 'tasks'] as const;
const ONLINE_QUEUE_BLOCK_MS = 5000;

type EnqueueOnlineStandardizationResult = {
  accepted: boolean;
  duplicate: boolean;
  aheadCount: number;
};

interface StandardizationQueueContextValue {
  queueItems: OnlineStandardizationQueueItem[];
  taskRecords: StandardizationTaskRecord[];
  onlineRunningTask: StandardizationTaskRecord | null;
  offlineProcessingTasks: StandardizationTaskRecord[];
  enqueueOnlineTask: (payload: OnlineStandardizationQueuePayload) => EnqueueOnlineStandardizationResult;
  retryQueueItem: (id: string) => void;
  removeQueueItem: (id: string) => void;
}

const StandardizationQueueContext = createContext<StandardizationQueueContextValue | null>(null);

function getTaskSummary(task: StandardizationTaskRecord) {
  return task.current_phase || task.current_item || '处理中';
}

function isMissingQueueSourceError(message: string) {
  return message.includes('没有采购物料') || message.includes('未找到符合条件的采购物料');
}

const FAILURE_SOUND_OPTION = { id: 'failure', label: '失败提示音', src: '/sounds/manual-intervention-library/alert-15.mp3' };

export function StandardizationQueueProvider({ children }: { children: ReactNode }) {
  const queryClient = useQueryClient();
  const [queueItems, setQueueItems] = useState<OnlineStandardizationQueueItem[]>(() => getStoredOnlineStandardizationQueue());
  const dispatchingRef = useRef(false);
  const unmountedRef = useRef(false);
  const [dispatchTick, setDispatchTick] = useState(() => Date.now());
  const hasSupabase = isSupabaseConfigured();
  const completionAudioRef = useRef<HTMLAudioElement | null>(null);
  const failureAudioRef = useRef<HTMLAudioElement | null>(null);
  const previousTaskStatusesRef = useRef<Record<string, string>>({});

  useEffect(() => {
    return () => {
      unmountedRef.current = true;
    };
  }, []);

  const { data: taskRecords = [] } = useQuery({
    queryKey: STANDARDIZATION_MONITOR_QUERY_KEY,
    queryFn: () => fetchStandardizationTasks(),
    enabled: hasSupabase,
    refetchInterval: (query) => {
      const data = (query.state.data as StandardizationTaskRecord[] | undefined) ?? [];
      const hasLiveTasks = data.some((task) => task.status === 'pending' || task.status === 'processing' || task.status === 'stopping');
      return queueItems.length > 0 || hasLiveTasks ? 2000 : 10000;
    },
    refetchIntervalInBackground: true,
  });

  const onlineRunningTask = useMemo(
    () => taskRecords.find((task) => task.execution_mode === 'online' && (task.status === 'processing' || task.status === 'stopping')) ?? null,
    [taskRecords],
  );
  const offlineProcessingTasks = useMemo(
    () => taskRecords.filter((task) => task.execution_mode === 'offline' && task.status === 'processing'),
    [taskRecords],
  );

  useEffect(() => {
    saveStoredOnlineStandardizationQueue(queueItems);
  }, [queueItems]);

  useEffect(() => {
    const prev = previousTaskStatusesRef.current;
    const activeStatuses = new Set(['pending', 'processing', 'stopping']);
    const hasNewlyCompleted = taskRecords.some(
      (task) => task.status === 'completed' && activeStatuses.has(prev[task.batch_id] ?? ''),
    );
    const hasNewlyFailed = taskRecords.some(
      (task) => task.status === 'failed' && activeStatuses.has(prev[task.batch_id] ?? ''),
    );
    previousTaskStatusesRef.current = Object.fromEntries(taskRecords.map((task) => [task.batch_id, task.status]));
    if (hasNewlyCompleted) {
      const soundId = normalizeManualInterventionAlertSoundId(
        typeof window !== 'undefined' ? window.localStorage.getItem(MANUAL_INTERVENTION_ALERT_SOUND_STORAGE_KEY) : null,
      );
      void playAlertSound(completionAudioRef, getManualInterventionAlertSoundOption(soundId));
    }
    if (hasNewlyFailed) {
      void playAlertSound(failureAudioRef, FAILURE_SOUND_OPTION);
    }
  }, [taskRecords]);

  useEffect(() => {
    const nextReadyAt = queueItems
      .map((item) => (item.status === 'queued' && item.blockedUntil ? Date.parse(item.blockedUntil) : Number.NaN))
      .filter((value) => Number.isFinite(value) && value > Date.now())
      .sort((left, right) => left - right)[0];

    if (!Number.isFinite(nextReadyAt)) {
      return;
    }

    const timeout = window.setTimeout(() => {
      setDispatchTick(Date.now());
    }, Math.max(0, nextReadyAt - Date.now()));

    return () => {
      window.clearTimeout(timeout);
    };
  }, [queueItems]);

  useEffect(() => {
    if (dispatchingRef.current) {
      return;
    }

    const hasOrphanSubmittingItem = queueItems.some((item) => item.status === 'submitting');
    if (!hasOrphanSubmittingItem) {
      return;
    }

    setQueueItems((current) => current.map((item) => (
      item.status === 'submitting'
        ? { ...item, status: 'queued', blockedUntil: null }
        : item
    )));
    setDispatchTick(Date.now());
  }, [queueItems]);

  useEffect(() => {
    const nextItem = queueItems.find((item) => (
      item.status === 'queued'
      && (!item.blockedUntil || Date.parse(item.blockedUntil) <= Date.now())
    ));

    if (!nextItem || onlineRunningTask || dispatchingRef.current) {
      return;
    }

    dispatchingRef.current = true;
    setQueueItems((current) => current.map((item) => (
      item.id === nextItem.id
        ? { ...item, status: 'submitting', errorMessage: '', blockedUntil: null }
        : item
    )));

    void (async () => {
      try {
        const response = await triggerStandardizeBatch({
          batchId: nextItem.batchId,
          materialIds: nextItem.materialIds,
          materials: nextItem.materials,
        });

        if (unmountedRef.current) {
          return;
        }

        setQueueItems((current) => current.filter((item) => item.id !== nextItem.id));
        await queryClient.invalidateQueries({ queryKey: STANDARDIZATION_MONITOR_QUERY_KEY });
        toast.success(response.message || `在线标准化已开始：${nextItem.batchId}`, {
          duration: 3500,
          position: 'bottom-right',
        });
      } catch (error) {
        if (unmountedRef.current) {
          return;
        }

        const message = error instanceof Error ? error.message : String(error);
        if (message.includes('已有在线标准化任务正在执行')) {
          setQueueItems((current) => current.map((item) => (
            item.id === nextItem.id
              ? {
                  ...item,
                  status: 'queued',
                  errorMessage: '',
                  blockedUntil: new Date(Date.now() + ONLINE_QUEUE_BLOCK_MS).toISOString(),
                }
              : item
          )));
          await queryClient.invalidateQueries({ queryKey: STANDARDIZATION_MONITOR_QUERY_KEY });
          return;
        }

        if (isMissingQueueSourceError(message)) {
          setQueueItems((current) => current.filter((item) => item.id !== nextItem.id));
          toast.error(`在线排队任务已自动移除：${nextItem.batchId} 的源物料已不存在`, {
            duration: 5000,
            position: 'bottom-right',
          });
          return;
        }

        setQueueItems((current) => current.map((item) => (
          item.id === nextItem.id
            ? { ...item, status: 'failed', errorMessage: message, blockedUntil: null }
            : item
        )));
        toast.error(`在线标准化排队任务提交失败：${message}`, {
          duration: 5000,
          position: 'bottom-right',
        });
      } finally {
        dispatchingRef.current = false;
        if (!unmountedRef.current) {
          setDispatchTick(Date.now());
        }
      }
    })();
  }, [dispatchTick, onlineRunningTask, queryClient, queueItems]);

  function enqueueOnlineTask(payload: {
    batchId: string;
    materialIds?: string[];
    materials?: StandardizeMaterialInput[];
    totalRows: number;
  }): EnqueueOnlineStandardizationResult {
    const fingerprint = buildOnlineStandardizationQueueFingerprint(payload);
    const activeDuplicate = queueItems.find((item) => item.fingerprint === fingerprint && item.status !== 'failed');
    if (activeDuplicate) {
      const aheadCount = queueItems
        .filter((item) => item.status !== 'failed')
        .findIndex((item) => item.id === activeDuplicate.id) + (onlineRunningTask ? 1 : 0);
      return {
        accepted: false,
        duplicate: true,
        aheadCount: Math.max(0, aheadCount),
      };
    }

    const failedDuplicate = queueItems.find((item) => item.fingerprint === fingerprint && item.status === 'failed');
    if (failedDuplicate) {
      setQueueItems((current) => current.map((item) => (
        item.id === failedDuplicate.id
          ? {
              ...item,
              status: 'queued',
              errorMessage: '',
              blockedUntil: null,
              createdAt: new Date().toISOString(),
            }
          : item
      )));
      return {
        accepted: true,
        duplicate: false,
        aheadCount: queueItems.filter((item) => item.status !== 'failed').length + (onlineRunningTask ? 1 : 0),
      };
    }

    const queueItem: OnlineStandardizationQueueItem = {
      id: crypto.randomUUID(),
      fingerprint,
      batchId: payload.batchId,
      materialIds: payload.materialIds,
      materials: payload.materials,
      totalRows: payload.totalRows,
      createdAt: new Date().toISOString(),
      status: 'queued',
      errorMessage: '',
      blockedUntil: null,
    };

    setQueueItems((current) => [...current, queueItem]);
    return {
      accepted: true,
      duplicate: false,
      aheadCount: currentQueueAheadCount(queueItems, null, onlineRunningTask),
    };
  }

  function retryQueueItem(id: string) {
    setQueueItems((current) => current.map((item) => (
      item.id === id
        ? { ...item, status: 'queued', errorMessage: '', blockedUntil: null }
        : item
    )));
    setDispatchTick(Date.now());
  }

  function removeQueueItem(id: string) {
    setQueueItems((current) => current.filter((item) => item.id !== id));
  }

  return (
    <StandardizationQueueContext.Provider
      value={{
        queueItems,
        taskRecords,
        onlineRunningTask,
        offlineProcessingTasks,
        enqueueOnlineTask,
        retryQueueItem,
        removeQueueItem,
      }}
    >
      {children}
    </StandardizationQueueContext.Provider>
  );
}

function currentQueueAheadCount(
  queueItems: OnlineStandardizationQueueItem[],
  itemId: string | null,
  onlineRunningTask: StandardizationTaskRecord | null,
) {
  const activeQueue = queueItems.filter((item) => item.status !== 'failed');
  if (!itemId) {
    return activeQueue.length + (onlineRunningTask ? 1 : 0);
  }

  const index = activeQueue.findIndex((item) => item.id === itemId);
  return Math.max(0, index + (onlineRunningTask ? 1 : 0));
}

export function useStandardizationQueue() {
  const context = useContext(StandardizationQueueContext);
  if (!context) {
    throw new Error('useStandardizationQueue 必须在 StandardizationQueueProvider 内使用');
  }

  return context;
}

export function formatStandardizationTaskSummary(task: StandardizationTaskRecord) {
  return `${task.batch_id} · ${getTaskSummary(task)}`;
}