import type {ChatTimelineItem} from '@easyflow/common-ui';
import {ChatTimelineBuilder} from '@easyflow/common-ui';

import type {AgentChatCapabilityPayload} from './api';
import {generateAgentSessionId, sendAgentChat, stopAgentChatStream,} from './api';

import {applyAgentSseEnvelope, parseAgentSseMessage,} from './adapters/agentTimelineAdapter';

interface RuntimeSessionState {
  agentId: string;
  agentName?: string;
  completed: boolean;
  error?: string;
  items: ChatTimelineItem[];
  prompt: string;
  roundId: string;
  sending: boolean;
  sessionId: string;
  updatedAt: number;
}

interface StoredRuntimeSession {
  agentId: string;
  agentName?: string;
  completed: boolean;
  error?: string;
  items: ChatTimelineItem[];
  prompt: string;
  roundId: string;
  sessionId: string;
  updatedAt: number;
  version: number;
}

interface StartOptions {
  agentId: string;
  agentName?: string;
  baseItems?: ChatTimelineItem[];
  capabilities?: AgentChatCapabilityPayload[];
  prompt: string;
  sessionId?: string;
}

const STORAGE_PREFIX = 'easyflow:agent-chat-runtime';
const LATEST_STORAGE_KEY = `${STORAGE_PREFIX}:latest`;
const STORAGE_VERSION = 1;

const sessions = new Map<string, RuntimeSessionState>();
const listeners = new Set<() => void>();
let latestSessionId = '';

function clone<T>(value: T): T {
  const serialized = JSON.stringify(value);
  return JSON.parse(serialized) as T;
}

function createRoundId() {
  return `agent-chat-round-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
}

function storageKey(sessionId: string) {
  return `${STORAGE_PREFIX}:${sessionId}`;
}

function safeSessionStorage() {
  try {
    return globalThis.sessionStorage;
  } catch {
    return undefined;
  }
}

function notify() {
  for (const listener of listeners) {
    listener();
  }
}

function persistSession(state: RuntimeSessionState) {
  const storage = safeSessionStorage();
  if (!storage) {
    return;
  }
  const snapshot: StoredRuntimeSession = {
    agentId: state.agentId,
    agentName: state.agentName,
    completed: state.completed,
    error: state.error,
    items: clone(state.items),
    prompt: state.prompt,
    roundId: state.roundId,
    sessionId: state.sessionId,
    updatedAt: state.updatedAt,
    version: STORAGE_VERSION,
  };
  try {
    storage.setItem(storageKey(state.sessionId), JSON.stringify(snapshot));
    storage.setItem(LATEST_STORAGE_KEY, state.sessionId);
  } catch {
    // 缓存失败不影响正式聊天主流程。
  }
}

function restoreSession(sessionId: string) {
  const existing = sessions.get(sessionId);
  if (existing) {
    return existing;
  }
  const storage = safeSessionStorage();
  if (!storage) {
    return undefined;
  }
  try {
    const raw = storage.getItem(storageKey(sessionId));
    if (!raw) {
      return undefined;
    }
    const parsed = JSON.parse(raw) as StoredRuntimeSession;
    if (parsed.version !== STORAGE_VERSION || parsed.sessionId !== sessionId) {
      return undefined;
    }
    const restored: RuntimeSessionState = {
      agentId: parsed.agentId,
      agentName: parsed.agentName,
      completed: parsed.completed,
      error: parsed.error,
      items: Array.isArray(parsed.items) ? parsed.items : [],
      prompt: parsed.prompt,
      roundId: parsed.roundId,
      sending: false,
      sessionId,
      updatedAt: parsed.updatedAt,
    };
    sessions.set(sessionId, restored);
    return restored;
  } catch {
    return undefined;
  }
}

function upsertState(state: RuntimeSessionState) {
  state.updatedAt = Date.now();
  latestSessionId = state.sessionId;
  sessions.set(state.sessionId, state);
  persistSession(state);
  notify();
}

function runningSession() {
  return [...sessions.values()].find((session) => session.sending);
}

function restoreLatestSession() {
  const running = runningSession();
  if (running) {
    return running;
  }
  const storage = safeSessionStorage();
  const storedSessionId = storage?.getItem(LATEST_STORAGE_KEY) || '';
  const sessionId = latestSessionId || storedSessionId;
  return sessionId ? restoreSession(sessionId) : undefined;
}

async function resolveSessionId(sessionId?: string) {
  if (sessionId) {
    return sessionId;
  }
  const res = await generateAgentSessionId();
  if (res.errorCode !== 0 || !res.data) {
    throw new Error(res.message || '会话创建失败');
  }
  return String(res.data);
}

function errorMessage(error: unknown) {
  return error instanceof Error ? error.message : '发送失败，请稍后再试';
}

export const agentChatRuntimeManager = {
  getSnapshot(sessionId?: string) {
    if (!sessionId) {
      return undefined;
    }
    const state = restoreSession(sessionId);
    return state ? clone(state) : undefined;
  },

  getLatestSnapshot() {
    const state = restoreLatestSession();
    return state ? clone(state) : undefined;
  },

  hasRunning() {
    return Boolean(runningSession());
  },

  replaceItems(sessionId: string, items: ChatTimelineItem[]) {
    const state = restoreSession(sessionId);
    if (!state) {
      return;
    }
    state.items = clone(items);
    upsertState(state);
  },

  async start(options: StartOptions) {
    const active = runningSession();
    if (active) {
      throw new Error('当前回复完成后再发送新消息');
    }
    const sessionId = await resolveSessionId(options.sessionId);
    const roundId = createRoundId();
    const state: RuntimeSessionState = {
      agentId: options.agentId,
      agentName: options.agentName,
      completed: false,
      items: clone(options.baseItems || []),
      prompt: options.prompt,
      roundId,
      sending: true,
      sessionId,
      updatedAt: Date.now(),
    };
    ChatTimelineBuilder.appendUserMessage(state.items, options.prompt, {
      roundId,
    });
    upsertState(state);

    void sendAgentChat(
      {
        agentId: options.agentId,
        capabilities: options.capabilities,
        prompt: options.prompt,
        sessionId,
      },
      {
        onError(error) {
          const current = sessions.get(sessionId);
          if (!current || !current.sending) {
            return;
          }
          current.error = errorMessage(error);
          current.sending = false;
          current.completed = true;
          ChatTimelineBuilder.appendError(current.items, current.error);
          ChatTimelineBuilder.finalize(current.items);
          upsertState(current);
        },
        onFinished() {
          const current = sessions.get(sessionId);
          if (!current) {
            return;
          }
          current.sending = false;
          current.completed = true;
          ChatTimelineBuilder.finalize(current.items);
          upsertState(current);
        },
        onMessage(message) {
          const current = sessions.get(sessionId);
          if (!current || !current.sending) {
            return;
          }
          const envelope = parseAgentSseMessage(message);
          if (!envelope) {
            return;
          }
          applyAgentSseEnvelope(current.items, envelope, { roundId });
          upsertState(current);
        },
      },
    );

    return sessionId;
  },

  stop(sessionId?: string) {
    const state = sessionId ? restoreSession(sessionId) : runningSession();
    if (!state || !state.sending) {
      return;
    }
    stopAgentChatStream();
    state.sending = false;
    state.completed = true;
    ChatTimelineBuilder.finalize(state.items);
    upsertState(state);
  },

  subscribe(listener: () => void) {
    listeners.add(listener);
    return () => {
      listeners.delete(listener);
    };
  },
};
