import { resource, tapState, tapMemo } from "@assistant-ui/tap";
import {
  type ClientOutput,
  tapClientLookup,
  Derived,
  attachTransformScopes,
  tapClientResource,
} from "@assistant-ui/store";
import { withKey } from "@assistant-ui/tap";
import type { ResourceElement } from "@assistant-ui/tap";

import { ModelContext, Suggestions } from "@assistant-ui/core/store";
import { Tools, DataRenderers } from "@assistant-ui/core/react";

const RESOLVED_PROMISE = Promise.resolve();

export type InMemoryThreadListProps = {
  thread: (threadId: string) => ResourceElement<ClientOutput<"thread">>;
  onSwitchToThread?: (threadId: string) => void;
  onSwitchToNewThread?: () => void;
};

type ThreadData = {
  id: string;
  title?: string;
  status: "regular" | "archived";
};

// ThreadListItem Client
const ThreadListItemClient = resource(
  (props: {
    data: ThreadData;
    onSwitchTo: () => void;
    onArchive: () => void;
    onUnarchive: () => void;
    onDelete: () => void;
  }): ClientOutput<"threadListItem"> => {
    const { data, onSwitchTo, onArchive, onUnarchive, onDelete } = props;
    const state = tapMemo(
      () => ({
        id: data.id,
        remoteId: undefined,
        externalId: undefined,
        title: data.title,
        status: data.status,
      }),
      [data.id, data.title, data.status],
    );

    return {
      getState: () => state,
      switchTo: onSwitchTo,
      rename: () => {},
      archive: onArchive,
      unarchive: onUnarchive,
      delete: onDelete,
      generateTitle: () => {},
      initialize: async () => ({ remoteId: data.id, externalId: undefined }),
      detach: () => {},
    };
  },
);

// InMemoryThreadList Client
export const InMemoryThreadList = resource(
  (props: InMemoryThreadListProps): ClientOutput<"threads"> => {
    const {
      thread: threadFactory,
      onSwitchToThread,
      onSwitchToNewThread,
    } = props;

    const [mainThreadId, setMainThreadId] = tapState("main");
    const [threads, setThreads] = tapState<readonly ThreadData[]>(() => [
      { id: "main", title: "Main Thread", status: "regular" },
    ]);

    const handleSwitchToThread = (threadId: string) => {
      setMainThreadId(threadId);
      onSwitchToThread?.(threadId);
    };

    const handleArchive = (threadId: string) => {
      setThreads((prev) =>
        prev.map((t) =>
          t.id === threadId ? { ...t, status: "archived" as const } : t,
        ),
      );
    };

    const handleUnarchive = (threadId: string) => {
      setThreads((prev) =>
        prev.map((t) =>
          t.id === threadId ? { ...t, status: "regular" as const } : t,
        ),
      );
    };

    const handleDelete = (threadId: string) => {
      setThreads((prev) => prev.filter((t) => t.id !== threadId));
      if (mainThreadId === threadId) {
        const remaining = threads.filter((t) => t.id !== threadId);
        setMainThreadId(remaining[0]?.id || "main");
      }
    };

    const handleSwitchToNewThread = () => {
      const newId = `thread-${Date.now()}`;
      setThreads((prev) => [
        ...prev,
        { id: newId, title: "New Thread", status: "regular" },
      ]);
      setMainThreadId(newId);
      onSwitchToNewThread?.();
    };

    const threadListItems = tapClientLookup(
      () =>
        threads.map((t) =>
          withKey(
            t.id,
            ThreadListItemClient({
              data: t,
              onSwitchTo: () => handleSwitchToThread(t.id),
              onArchive: () => handleArchive(t.id),
              onUnarchive: () => handleUnarchive(t.id),
              onDelete: () => handleDelete(t.id),
            }),
          ),
        ),
      [threads],
    );

    // Create the main thread
    const mainThreadClient = tapClientResource(threadFactory(mainThreadId));

    const state = tapMemo(() => {
      const regularThreads = threads.filter((t) => t.status === "regular");
      const archivedThreads = threads.filter((t) => t.status === "archived");

      return {
        mainThreadId,
        newThreadId: null,
        isLoading: false,
        threadIds: regularThreads.map((t) => t.id),
        archivedThreadIds: archivedThreads.map((t) => t.id),
        threadItems: threadListItems.state,
        main: mainThreadClient.state,
      };
    }, [mainThreadId, threads, threadListItems.state, mainThreadClient.state]);

    return {
      getState: () => state,
      switchToThread: handleSwitchToThread,
      switchToNewThread: handleSwitchToNewThread,
      getLoadThreadsPromise: () => RESOLVED_PROMISE,
      reload: () => RESOLVED_PROMISE,
      item: (selector) => {
        if (selector === "main") {
          const index = threads.findIndex((t) => t.id === mainThreadId);
          return threadListItems.get({ index: index === -1 ? 0 : index });
        }
        if ("id" in selector) {
          const index = threads.findIndex((t) => t.id === selector.id);
          return threadListItems.get({ index });
        }
        return threadListItems.get(selector);
      },
      thread: () => mainThreadClient.methods,
    };
  },
);

attachTransformScopes(InMemoryThreadList, (scopes, parent) => {
  scopes.thread ??= Derived({
    source: "threads",
    query: { type: "main" },
    get: (aui) => aui.threads().thread("main"),
  });
  scopes.threadListItem ??= Derived({
    source: "threads",
    query: { type: "main" },
    get: (aui) => aui.threads().item("main"),
  });
  scopes.composer ??= Derived({
    source: "thread",
    query: {},
    get: (aui) => aui.threads().thread("main").composer(),
  });

  if (!scopes.modelContext && parent.modelContext.source === null) {
    scopes.modelContext = ModelContext();
  }
  if (!scopes.tools && parent.tools.source === null) {
    scopes.tools = Tools({});
  }
  if (!scopes.dataRenderers && parent.dataRenderers.source === null) {
    scopes.dataRenderers = DataRenderers();
  }
  if (!scopes.suggestions && parent.suggestions.source === null) {
    scopes.suggestions = Suggestions();
  }
});
