import { appendEvent, coalesceEvents } from "../../event-stream.js";

export const initialConversationState = { sessionId: "", events: [], message: "", loading: false };

export function reduceConversationState(state, action) {
  if (action.type === "session.replaced") return {
    sessionId: action.sessionId,
    events: coalesceEvents(action.events ?? []),
    message: action.message ?? "",
    loading: action.loaded !== true
  };
  if (action.type === "snapshot.received") return { ...state, events: mergeSnapshotEvents(state.events, action.events ?? []), loading: false };
  if (action.type === "event.received") {
    const { events, entry } = replaceMatchingOptimisticEvent(state.events, action.entry);
    return { ...state, events: appendEvent(events, entry), loading: false };
  }
  if (action.type === "events.received") {
    if (action.sessionId !== state.sessionId || (action.entries ?? []).length === 0) return state;
    const events = action.entries.reduce((current, incoming) => {
      const replaced = replaceMatchingOptimisticEvent(current, incoming);
      return appendEvent(replaced.events, replaced.entry);
    }, state.events);
    return { ...state, events, loading: false };
  }
  if (action.type === "message.changed") return { ...state, message: action.message };
  if (action.type === "message.submitted") {
    return {
      ...state,
      message: "",
      events: appendEvent(state.events, {
        id: action.id,
        event: "message.accepted",
        data: {
          text: action.text,
          ...(action.queued ? { queued: true } : {}),
          ...(action.attachmentCount ? { attachmentCount: action.attachmentCount } : {}),
          ...(action.attachments?.length ? { attachments: safeBrowserAttachments(action.attachments) } : {}),
          optimistic: true
        },
        createdAt: action.createdAt
      })
    };
  }
  if (action.type === "message.failed") {
    return {
      ...state,
      message: action.text,
      events: state.events.filter((event) => event.id !== action.id)
    };
  }
  if (action.type === "message.queued") {
    return action.keepOptimistic ? state : { ...state, events: state.events.filter((event) => event.id !== action.id) };
  }
  if (action.type === "message.sent") return { ...state, message: "" };
  if (action.type === "approval.decision.started") {
    return {
      ...state,
      events: appendEvent(state.events, {
        id: action.eventId,
        event: "approval.resolved",
        data: {
          ...latestApprovalData(state.events, action.id),
          id: action.id,
          status: action.decision,
          optimistic: true
        },
        createdAt: action.createdAt
      })
    };
  }
  if (action.type === "approval.decision.failed") {
    return {
      ...state,
      events: appendEvent(state.events, {
        id: action.eventId,
        event: "approval.requested",
        data: {
          ...latestApprovalData(state.events, action.id),
          id: action.id,
          status: "pending",
          optimistic: true
        },
        createdAt: action.createdAt
      })
    };
  }
  if (action.type === "stop.requested") {
    return {
      ...state,
      events: appendEvent(state.events, {
        id: action.id,
        event: "session.status",
        data: { status: "interrupted", optimistic: true },
        createdAt: action.createdAt
      })
    };
  }
  if (action.type === "stop.failed") return { ...state, events: state.events.filter((event) => event.id !== action.id) };
  return state;
}

export function sessionOwnerId(session, fallbackAccountId) {
  return session?.accountId ?? fallbackAccountId;
}

function replaceMatchingOptimisticEvent(events, entry) {
  if (entry?.event !== "message.accepted" || entry.data?.optimistic) return { events, entry };
  const index = events.findIndex((event) =>
    event.event === "message.accepted"
    && event.data?.optimistic
    && event.data?.text === entry.data?.text
    && (event.data?.attachmentCount ?? 0) === (entry.data?.attachmentCount ?? 0)
  );
  if (index < 0) return { events, entry };
  const optimistic = events[index];
  const mergedEntry = {
    ...entry,
    id: optimistic.id,
    createdAt: optimistic.createdAt,
    data: {
      ...entry.data,
      optimisticAccepted: true,
      acceptedAt: entry.createdAt,
      ...(optimistic.data?.attachments?.length && !entry.data?.attachments?.length
        ? { attachments: optimistic.data.attachments }
        : {})
    }
  };
  return { events: events.filter((_, eventIndex) => eventIndex !== index), entry: mergedEntry };
}

function mergeSnapshotEvents(currentEvents, snapshotEvents) {
  const snapshot = coalesceEvents(snapshotEvents);
  const pendingOptimistic = currentEvents.filter((event) => event.data?.optimistic && !snapshotSupersedes(snapshot, event));
  return coalesceEvents([...snapshot, ...pendingOptimistic]);
}

function snapshotSupersedes(snapshot, optimisticEvent) {
  if (optimisticEvent.event === "message.accepted") {
    return snapshot.some((event) =>
      event.event === "message.accepted"
      && !event.data?.optimistic
      && event.data?.text === optimisticEvent.data?.text
      && (event.data?.attachmentCount ?? 0) === (optimisticEvent.data?.attachmentCount ?? 0)
    );
  }
  if (optimisticEvent.event === "session.status") {
    return snapshot.some((event) =>
      event.event === "session.status"
      && !event.data?.optimistic
      && event.data?.status === optimisticEvent.data?.status
    );
  }
  return false;
}

function latestApprovalData(events, id) {
  return [...events].reverse().find((event) =>
    ["approval.requested", "approval.resolved"].includes(event.event)
    && event.data?.id === id
  )?.data ?? {};
}

function safeBrowserAttachments(attachments) {
  return attachments
    .filter((item) => item?.previewUrl)
    .map((item) => ({
      id: item.id,
      name: item.name,
      previewUrl: item.previewUrl
    }));
}
