import type { ServerSentEventMessage } from 'fetch-event-stream';

import type {
  ChatImageAttachment,
  ChatTimelineItem as ChatTimelineItemType,
  ChatTimelineMessageItem,
} from '@easyflow/common-ui';
import { ChatTimelineBuilder } from '@easyflow/common-ui';

import type {
  AgentInfo,
  AgentKnowledgeBinding,
  AgentToolBinding,
} from '../types';

import { ref } from 'vue';

import { sseClient } from '#/api/request';

import { clearAgentDraftSession } from '../api';
import { useAgentTryoutRawRounds } from './useAgentTryoutRawRounds';

function resolveDraftSessionId(agent: AgentInfo) {
  return `agent-draft-${agent.id || agent.localId || 'unsaved'}`;
}

function parseEventData(message: ServerSentEventMessage) {
  const raw = message.data || '';
  if (!raw) return {};
  try {
    return JSON.parse(raw);
  } catch {
    return { payload: { delta: raw } };
  }
}

function resolveEnvelope(data: any) {
  return {
    domain: data.domain || data.eventDomain || data.typeDomain,
    type: data.type || data.eventType || data.chatType || data.event,
    payload: data.payload ?? data.data ?? data,
  };
}

function asText(value: unknown) {
  return value === null || value === undefined ? '' : String(value);
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : {};
}

function isEndOfRoundEvent(domain: string, type: string) {
  return domain === 'SYSTEM' && type === 'DONE';
}

interface DraftRuntimeContext {
  agent: AgentInfo;
  knowledgeBindings: AgentKnowledgeBinding[];
  toolBindings: AgentToolBinding[];
}

export function useAgentTryoutStream() {
  const timelineItems = ref<ChatTimelineItemType[]>([]);
  const loading = ref(false);
  let rawRounds: ReturnType<typeof useAgentTryoutRawRounds> | undefined;
  let activeRoundId = '';
  let activeSessionId = '';
  let userStopped = false;

  function errorMessageOf(error: unknown) {
    if (error instanceof Error) {
      return `${error.name} ${error.message}`.trim();
    }
    if (typeof error === 'string') {
      return error;
    }
    const value = asRecord(error);
    const nested = asRecord(value.cause);
    return [value.name, value.message, value.error, nested.name, nested.message]
      .map((item) => asText(item).trim())
      .filter(Boolean)
      .join(' ');
  }

  function isAbortError(error: unknown) {
    const message = errorMessageOf(error).toLowerCase();
    return message.includes('abort');
  }

  function shouldIgnoreStoppedError(error: unknown) {
    return userStopped && isAbortError(error);
  }

  function finishStoppedRun() {
    finishAssistant();
    rawRounds?.flush();
    loading.value = false;
  }

  function rebuildTimeline() {
    timelineItems.value = rawRounds?.buildTimelineItems() || [];
  }

  function syncDraftContext(
    payload: DraftRuntimeContext,
    restore = false,
    requestedSessionId?: string,
  ) {
    const sessionId =
      requestedSessionId || resolveDraftSessionId(payload.agent);
    const sessionChanged = activeSessionId !== sessionId;
    activeSessionId = sessionId;
    if (!rawRounds || sessionChanged) {
      rawRounds = useAgentTryoutRawRounds({
        mode: 'draft',
        sessionId,
      });
      activeRoundId = '';
    }
    if (restore && sessionChanged && !loading.value) {
      rebuildTimeline();
    }
  }

  function markRoundCompleted(roundId: string) {
    if (!roundId) {
      return;
    }
    rawRounds?.completeRound(roundId);
    rebuildTimeline();
  }

  function finishAssistant() {
    ChatTimelineBuilder.finalize(timelineItems.value);
  }

  function markToolApproving(payload: {
    requestId?: string;
    resumeToken?: string;
    toolCallId?: string;
  }) {
    rawRounds?.recordEvent(activeRoundId, {
      domain: 'TOOL',
      payload,
      type: 'FORM_APPROVING',
    });
    rawRounds?.flush();
    rebuildTimeline();
  }

  function markToolRejected(payload: {
    reason?: string;
    requestId?: string;
    resumeToken?: string;
    toolCallId?: string;
  }) {
    rawRounds?.recordEvent(activeRoundId, {
      domain: 'TOOL',
      payload,
      type: 'FORM_REJECTED',
    });
    rawRounds?.flush();
    rebuildTimeline();
  }

  function handleMessage(message: ServerSentEventMessage) {
    const data = parseEventData(message);
    const envelope = resolveEnvelope(data);
    const domain = String(envelope.domain || '').toUpperCase();
    const type = String(envelope.type || '').toUpperCase();
    const payload = envelope.payload || {};

    if (activeRoundId) {
      rawRounds?.recordEvent(activeRoundId, {
        domain,
        payload,
        type,
      });
      rebuildTimeline();
    }

    if (domain === 'LLM' && type === 'MESSAGE') {
      return;
    }
    if (domain === 'LLM' && type === 'THINKING') {
      const text = asText(payload.reasoning ?? payload.delta ?? payload.text);
      if (!text) return;
      return;
    }
    if (domain === 'TOOL' && type === 'FORM_REQUEST') {
      return;
    }
    if (domain === 'TOOL' && (type === 'TOOL_CALL' || type === 'TOOL_RESULT')) {
      return;
    }
    if (domain === 'BUSINESS' && type === 'CITATIONS') {
      return;
    }
    if (domain === 'BUSINESS' && type === 'STATUS') {
      return;
    }
    if (isEndOfRoundEvent(domain, type)) {
      markRoundCompleted(activeRoundId);
      return;
    }
    if (type === 'ERROR' || domain === 'ERROR') {
      const message = payload.message ?? payload.error ?? '试运行失败';
      if (shouldIgnoreStoppedError(message)) {
        return;
      }
    }
  }

  async function runDraft(payload: {
    agent: AgentInfo;
    images?: ChatImageAttachment[];
    imageUploadIds?: string[];
    knowledgeBindings: AgentKnowledgeBinding[];
    onAccepted?: () => void | Promise<void>;
    prompt: string;
    sessionId?: string;
    toolBindings: AgentToolBinding[];
  }) {
    syncDraftContext(payload, false, payload.sessionId);
    if (!rawRounds) {
      return;
    }
    activeRoundId = rawRounds.createRound(payload.prompt, payload.images);
    rebuildTimeline();
    loading.value = true;
    userStopped = false;
    let accepted = false;
    await sseClient.post(
      '/api/v1/agent/chat/draft',
      {
        agent: payload.agent,
        imageUploadIds: payload.imageUploadIds,
        knowledgeBindings: payload.knowledgeBindings,
        prompt: payload.prompt,
        sessionId: activeSessionId,
        toolBindings: payload.toolBindings,
      },
      {
        onMessage: (message) => {
          const envelope = resolveEnvelope(parseEventData(message));
          const domain = String(envelope.domain || '').toUpperCase();
          const type = String(envelope.type || '').toUpperCase();
          if (!accepted && domain === 'SYSTEM' && type === 'INPUT_ACCEPTED') {
            accepted = true;
            void payload.onAccepted?.();
          }
          handleMessage(message);
        },
        onError: (error) => {
          if (shouldIgnoreStoppedError(error)) {
            return;
          }
          rawRounds?.recordEvent(activeRoundId, {
            domain: 'SYSTEM',
            payload: {
              message: error?.message ?? '试运行失败，请稍后再试',
            },
            type: 'ERROR',
          });
          rebuildTimeline();
          finishAssistant();
          rawRounds?.flush();
          loading.value = false;
        },
        onFinished: () => {
          if (userStopped) {
            return;
          }
          finishAssistant();
          markRoundCompleted(activeRoundId);
          rawRounds?.flush();
          loading.value = false;
        },
      },
    );
  }

  async function sendDraft(payload: {
    agent: AgentInfo;
    images?: ChatImageAttachment[];
    imageUploadIds?: string[];
    knowledgeBindings: AgentKnowledgeBinding[];
    onAccepted?: () => void | Promise<void>;
    prompt: string;
    sessionId?: string;
    toolBindings: AgentToolBinding[];
  }) {
    await runDraft(payload);
  }

  async function regenerateDraft(item: ChatTimelineMessageItem) {
    // 有状态 runtime 的历史重生成需要后端 session fork/rollback，第一版先禁用入口。
    void item;
  }

  function selectVariant(
    item: ChatTimelineMessageItem,
    direction: 'next' | 'previous',
  ) {
    if (!item.roundId || !rawRounds?.canSwitch(item, direction)) {
      return;
    }
    rawRounds.selectVariant(item.roundId, direction);
    rebuildTimeline();
  }

  function copyMessageText(item: ChatTimelineMessageItem) {
    return rawRounds?.copyText(item) || '';
  }

  async function clearDraftSession() {
    if (loading.value) {
      userStopped = true;
      sseClient.abort();
      loading.value = false;
    }
    const sessionId = activeSessionId;
    rawRounds?.clear();
    timelineItems.value = [];
    activeRoundId = '';
    if (sessionId) {
      await clearAgentDraftSession(sessionId);
    }
  }

  function stop() {
    if (!loading.value) {
      return;
    }
    userStopped = true;
    sseClient.abort();
    finishStoppedRun();
  }

  return {
    loading,
    clearDraftSession,
    markToolApproving,
    markToolRejected,
    copyMessageText,
    regenerateDraft,
    selectVariant,
    syncDraftContext,
    timelineItems,
    sendDraft,
    stop,
  };
}
