import { BUILTIN_AGENT_SLUGS } from '@lobechat/builtin-agents';
import { CUSTOM_DOCUMENT_FILE_TYPE } from '@lobechat/const';

import { stableWorkspaceAwareNavigate } from '@/features/Workspace/stableWorkspaceAwareNavigate';
import { chatGroupService } from '@/services/chatGroup';
import { documentService } from '@/services/document';
import { getAgentStoreState } from '@/store/agent';
import { agentSelectors, builtinAgentSelectors } from '@/store/agent/selectors';
import { getChatGroupStoreState } from '@/store/agentGroup';
import { useChatStore } from '@/store/chat';
import { useGlobalStore } from '@/store/global';
import { useGroupProfileStore } from '@/store/groupProfile';
import { type HomeStore } from '@/store/home/store';
import { type StoreSetter } from '@/store/types';
import { markdownToTxt } from '@/utils/markdownToTxt';
import { setNamespace } from '@/utils/storeDebug';

import { type StarterMode } from './initialState';

const n = setNamespace('homeInput');

interface SendMessageWithEditorParams {
  editorData?: Record<string, any>;
  groupId?: string;
  message: string;
}

/**
 * Make sure a builtin agent (agent-builder / group-agent-builder / page-agent)
 * is hydrated into both `builtinAgentIdMap` and `agentMap` before we read its
 * id and call sendMessage. Without this, the create-Agent / create-Group /
 * create-Page flows can race against the host page's `useInitBuiltinAgent`:
 * `builtinAgentIdMap[slug]` is still undefined, so sendMessage gets
 * `agentId: undefined` and silently early-returns. Symptom: navigation lands
 * on the builder page but the conversation never starts.
 */
const ensureBuiltinAgentHydrated = async (slug: string): Promise<string | undefined> => {
  const state = getAgentStoreState();
  const cachedId = state.builtinAgentIdMap[slug];
  if (cachedId && state.agentMap[cachedId]) return cachedId;

  await state.refreshBuiltinAgent(slug);
  return getAgentStoreState().builtinAgentIdMap[slug];
};

type Setter = StoreSetter<HomeStore>;
export const createHomeInputSlice = (set: Setter, get: () => HomeStore, _api?: unknown) =>
  new HomeInputActionImpl(set, get, _api);

export class HomeInputActionImpl {
  readonly #get: () => HomeStore;
  readonly #set: Setter;

  constructor(set: Setter, get: () => HomeStore, _api?: unknown) {
    void _api;
    this.#set = set;
    this.#get = get;
  }

  clearInputMode = (): void => {
    this.#set({ inputActiveMode: null }, false, n('clearInputMode'));
  };

  sendAsAgent = async ({
    editorData,
    groupId,
    message,
  }: SendMessageWithEditorParams): Promise<string> => {
    this.#set({ homeInputLoading: true }, false, n('sendAsAgent/start'));

    try {
      const agentState = getAgentStoreState();

      // 1. Get model/provider config from inbox agent
      const inboxAgentId = builtinAgentSelectors.inboxAgentId(agentState);
      const inboxConfig = inboxAgentId
        ? agentSelectors.getAgentConfigById(inboxAgentId)(agentState)
        : null;
      const model = inboxConfig?.model;
      const provider = inboxConfig?.provider;

      // 2. Create new Agent with inherited model/provider
      const result = await agentState.createAgent({
        config: {
          model,
          provider,
          systemRole: message,
          title: markdownToTxt(message ?? '').slice(0, 50) || 'New Agent',
        },
        groupId,
      });

      if (message.trim()) {
        useGlobalStore.getState().toggleAgentBuilderPanel(true);
      }

      // 3. Navigate to Agent profile page
      stableWorkspaceAwareNavigate(`/agent/${result.agentId}/profile`);

      // 4. Refresh agent list
      this.#get().refreshAgentList();

      // 5. Update agentBuilder's model config and send initial message
      if (result.agentId) {
        const { sendMessage } = useChatStore.getState();
        // Ensure agentBuilder is loaded before reading its id — the host
        // AgentBuilder component's useInitBuiltinAgent only fires after this
        // navigation completes, which would otherwise race with sendMessage.
        const agentBuilderId = await ensureBuiltinAgentHydrated(BUILTIN_AGENT_SLUGS.agentBuilder);

        // Update agentBuilder's model to match inbox selection
        if (agentBuilderId && model && provider) {
          await agentState.updateAgentConfigById(agentBuilderId, { model, provider });
        }

        if (agentBuilderId) {
          await sendMessage({
            context: { agentId: agentBuilderId, scope: 'agent_builder' },
            editorData,
            message,
          });
        }
      }

      // 6. Clear mode
      this.#set({ inputActiveMode: null }, false, n('sendAsAgent/clearMode'));

      return result.agentId!;
    } finally {
      this.#set({ homeInputLoading: false }, false, n('sendAsAgent/end'));
    }
  };

  sendAsGroup = async ({
    editorData,
    groupId,
    message,
  }: SendMessageWithEditorParams): Promise<string> => {
    this.#set({ homeInputLoading: true }, false, n('sendAsGroup/start'));

    try {
      const agentState = getAgentStoreState();

      // 1. Get model/provider config from inbox agent
      const inboxAgentId = builtinAgentSelectors.inboxAgentId(agentState);
      const inboxConfig = inboxAgentId
        ? agentSelectors.getAgentConfigById(inboxAgentId)(agentState)
        : null;
      const model = inboxConfig?.model;
      const provider = inboxConfig?.provider;

      // 2. Create new Group with inherited model/provider for orchestrator
      const { group } = await chatGroupService.createGroup({
        config: {
          systemPrompt: message,
        },
        groupId,
        title: markdownToTxt(message ?? '').slice(0, 50) || 'New Group',
      });

      // 3. Load groups and refresh
      const groupStore = getChatGroupStoreState();
      await groupStore.loadGroups();

      // 4. Refresh sidebar agent list
      this.#get().refreshAgentList();

      if (message.trim()) {
        useGroupProfileStore.getState().setChatPanelExpanded(true);
      }

      // 5. Navigate to Group profile page
      stableWorkspaceAwareNavigate(`/group/${group.id}/profile`);

      // 6. Update groupAgentBuilder's model config and send initial message.
      // Hydrate first so we don't race with the group profile page's own init.
      const groupAgentBuilderId = await ensureBuiltinAgentHydrated(
        BUILTIN_AGENT_SLUGS.groupAgentBuilder,
      );

      if (groupAgentBuilderId) {
        // Update groupAgentBuilder's model to match inbox selection
        if (model && provider) {
          await agentState.updateAgentConfigById(groupAgentBuilderId, { model, provider });
        }

        const { sendMessage } = useChatStore.getState();
        await sendMessage({
          context: { agentId: groupAgentBuilderId, scope: 'group_agent_builder' },
          editorData,
          message,
        });
      }

      // 7. Clear mode
      this.#set({ inputActiveMode: null }, false, n('sendAsGroup/clearMode'));

      return group.id;
    } finally {
      this.#set({ homeInputLoading: false }, false, n('sendAsGroup/end'));
    }
  };

  sendAsResearch = async (message: string): Promise<void> => {
    // TODO: Implement DeepResearch mode
    console.info('sendAsResearch:', message);

    // Clear mode
    this.#set({ inputActiveMode: null }, false, n('sendAsResearch'));
  };

  sendAsWrite = async ({ editorData, message }: SendMessageWithEditorParams): Promise<string> => {
    this.#set({ homeInputLoading: true }, false, n('sendAsWrite/start'));

    try {
      const agentState = getAgentStoreState();

      // 1. Get model/provider config from inbox agent
      const inboxAgentId = builtinAgentSelectors.inboxAgentId(agentState);
      const inboxConfig = inboxAgentId
        ? agentSelectors.getAgentConfigById(inboxAgentId)(agentState)
        : null;
      const model = inboxConfig?.model;
      const provider = inboxConfig?.provider;

      // 2. Create new Document
      const newDoc = await documentService.createDocument({
        editorData: '{}',
        fileType: CUSTOM_DOCUMENT_FILE_TYPE,
        title: markdownToTxt(message ?? '').slice(0, 50) || 'Untitled',
      });

      // 3. Navigate to Page
      stableWorkspaceAwareNavigate(`/page/${newDoc.id}`);

      // 4. Update pageAgent's model config and send initial message. Hydrate
      // first to avoid the same race the agent/group flows hit.
      const pageAgentId = await ensureBuiltinAgentHydrated(BUILTIN_AGENT_SLUGS.pageAgent);

      if (pageAgentId) {
        // Update pageAgent's model to match inbox selection
        if (model && provider) {
          await agentState.updateAgentConfigById(pageAgentId, { model, provider });
        }

        const { sendMessage } = useChatStore.getState();
        await sendMessage({
          // Pass the freshly created document id explicitly. The new PageEditor
          // has not mounted yet, so the page editor runtime singleton may still
          // be bound to the previously open document — relying on its fallback
          // here would scope server-side PageAgent tools to the wrong document.
          context: { agentId: pageAgentId, documentId: newDoc.id, scope: 'page' },
          editorData,
          message,
        });
      }

      // 5. Clear mode
      this.#set({ inputActiveMode: null }, false, n('sendAsWrite/clearMode'));

      return newDoc.id;
    } finally {
      this.#set({ homeInputLoading: false }, false, n('sendAsWrite/end'));
    }
  };

  setInputActiveMode = (mode: StarterMode): void => {
    this.#set({ inputActiveMode: mode }, false, n('setInputActiveMode', mode));
  };
}

export type HomeInputAction = Pick<HomeInputActionImpl, keyof HomeInputActionImpl>;
