import { Dispatch, MutableRefObject, SetStateAction, useCallback, useEffect, useId, useRef, useState } from 'react';
import useSWR, { KeyedMutator } from 'swr';
import { callChatApi } from '../shared/call-chat-api';
import { generateId as generateIdFunc } from '../shared/generate-id';
import { processChatStream } from '../shared/process-chat-stream';
import type {
  ChatRequest,
  ChatRequestOptions,
  CreateMessage,
  IdGenerator,
  JSONValue,
  Message,
  UseChatOptions,
  WebSocketMessage,
} from '../shared/types';
import type {
  ReactResponseRow,
  experimental_StreamingReactResponse,
} from '../streams/streaming-react-response';
import { readDataStream } from '../shared/read-data-stream';
export type { CreateMessage, Message, UseChatOptions };

export const ENDTXT = '|=EOF=|'

export type UseISDKHelpers = {
  /** Current messages in the chat */
  messages: Message[];
  /** The error object of the API request */
  error: undefined | Error;
  /**
   * Append a user message to the chat list. This triggers the API call to fetch
   * the assistant's response.
   * @param message The message to append
   * @param options Additional options to pass to the API call
   */
  append: (
    message: Message | CreateMessage,
    chatRequestOptions?: ChatRequestOptions,
  ) => Promise<string | null | undefined>;
  /**
   * Reload the last AI chat response for the given chat history. If the last
   * message isn't from the assistant, it will request the API to generate a
   * new response.
   */
  reload: (
    chatRequestOptions?: ChatRequestOptions,
  ) => Promise<string | null | undefined>;
  /**
   * Abort the current request immediately, keep the generated tokens if any.
   */
  stop: () => void;
  /**
   * Update the `messages` state locally. This is useful when you want to
   * edit the messages on the client, and then trigger the `reload` method
   * manually to regenerate the AI response.
   */
  setMessages: (messages: Message[]) => void;
  /** The current value of the input */
  input: string;
  /** setState-powered method to update the input value */
  setInput: React.Dispatch<React.SetStateAction<string>>;
  /** An input/textarea-ready onChange handler to control the value of the input */
  handleInputChange: (
    e:
      | React.ChangeEvent<HTMLInputElement>
      | React.ChangeEvent<HTMLTextAreaElement>,
  ) => void;
  /** Form submission handler to automatically reset input and append a user message */
  handleSubmit: (
    e: React.FormEvent<HTMLFormElement>,
    chatRequestOptions?: ChatRequestOptions,
  ) => void;
  metadata?: Object;
  /** Whether the API request is in progress */
  isLoading: boolean;
  isSocket: boolean;


  /** Additional data added on the server via StreamData */
  data?: JSONValue[];
};

export type IMessages = {
  action: string;
  type: string; //text: 文本聊天会话 audio: 语音聊天会话
  content: string;
}


type StreamingReactResponseAction = (payload: {
  messages: Message[];
  data?: Record<string, string>;
}) => Promise<experimental_StreamingReactResponse>;




const handleWebSocketMessage = async (

  event: MessageEvent<any>,
  messagesRef: React.MutableRefObject<Message[]>,
  isLoadingRef: MutableRefObject<boolean>,
  mutate: KeyedMutator<Message[]>,
  setInput: Dispatch<SetStateAction<string>>,
  mutateLoading: KeyedMutator<boolean>,
  generateId: IdGenerator,
  streamMode?: 'stream-data' | 'text',
  onFinish?: (message: Message) => void,
  onResponse?: (response: Response) => void | Promise<void>,
  onFunctionCall?: (
    functionCall: WebSocketMessage,
    // functionCall: FunctionCall,
    // onToolCall: (message: Message) => void
  ) => void | Promise<void>,


  // mutateStreamData: KeyedMutator<JSONValue[] | undefined>,
  // chatRequest: ChatRequest,
  // existingData: JSONValue[] | undefined,
  // extraMetadataRef: React.MutableRefObject<any>,

  // abortControllerRef: React.MutableRefObject<AbortController | null>,


) => {
  const receivedMessage = event.data;
  let parsedMessage = receivedMessage;


  // const reader = event.data.body.getReader(); // 获取数据流读取器

  mutate(messagesRef.current, false);
  const previousMessages = messagesRef.current;

  const length = previousMessages.length - 1
  // mutate(chatRequest.messages, false);

  try {
    parsedMessage = JSON.parse(receivedMessage);

    if (typeof parsedMessage == "object" && parsedMessage.type === 'function') {

      console.log("------请调用:", parsedMessage)


      if (onFunctionCall) {
        // // const functionCall = message.function_call;
        // // // Make sure functionCall is an object
        // // // If not, we got tool calls instead of function calls
        // // if (typeof functionCall !== 'object') {
        // //   console.warn(
        // //     'experimental_onFunctionCall should not be defined when using tools',
        // //   );
        // //   continue;
        // // }

        // // User handles the function call in their own functionCallHandler.
        // // The "arguments" key of the function call object will still be a string which will have to be parsed in the function handler.
        // // If the "arguments" JSON is malformed due to model error the user will have to handle that themselves.

        // const functionCallResponse: ChatRequest | void =
        //   await experimental_onFunctionCall(
        //     getCurrentMessages(),
        //     functionCall,
        //   );

        // // If the user does not return anything as a result of the function call, the loop will break.
        // if (functionCallResponse === undefined) {
        //   hasFollowingResponse = false;
        //   break;
        // }

        // // A function call response was returned.
        // // The updated chat with function call response will be sent to the API in the next iteration of the loop.
        // updateChatRequest(functionCallResponse);

        mutate([...previousMessages, {
          id: generateId(),
          content: "",
          role: 'assistant',
          createdAt: new Date(),
          function_call: parsedMessage.functionName
        }], false);

        await onFunctionCall(parsedMessage);


        if (onFinish) {
          onFinish(parsedMessage);
        }

      }

      return
    }
  } catch (error) {
    // console.error("解析接收到的消息时出错:", error);
  }

  let messageBuffer: Message = {
    id: generateId(),
    content: "",
    role: 'assistant',
    createdAt: new Date()
  };

  console.log("===================", isLoadingRef.current)

  if (streamMode === "text") {
    // V1 版本
    switch (parsedMessage.streamStatus) {
      case "start":
        mutateLoading(true);
        console.log("开始接收信息", parsedMessage);

        mutate([...previousMessages, messageBuffer], false);

        break;
      case "end":
        console.log("结束接收信息", parsedMessage);

        mutate([...previousMessages.slice(0, -1), {
          ...previousMessages[length],
          createdAt: new Date()
        }], false);
        setInput("")
        mutateLoading(false);
        break;
      default:


        // let lastMessage = previousMessages[length];
        // console.log("中间接收信息进行消息输出拼接", parsedMessage, previousMessages[length]);

        previousMessages[length].content = previousMessages[length].content + parsedMessage
        previousMessages[length].createdAt = new Date()
        // setMessages([...messagesRef.current.slice(0, -1), lastMessage])

        mutate([...previousMessages.slice(0, -1), ...[previousMessages[length]]], false);


        break;
    }
  } else {
    switch (parsedMessage) {

      case ENDTXT:
        console.log("结束接收信息", parsedMessage);

        mutate([...previousMessages.slice(0, -1), {
          ...previousMessages[length],
          createdAt: new Date()
        }], false);
        setInput("")
        isLoadingRef.current = false
        mutateLoading(false);
        break;
      default:
        if (!isLoadingRef.current) {
          isLoadingRef.current = true
          mutateLoading(true);
          console.log("开始接收信息", parsedMessage);

          messageBuffer.content = parsedMessage

          mutate([...previousMessages, messageBuffer], false);

        } else {
          // let lastMessage = previousMessages[length];
          // console.log("中间接收信息进行消息输出拼接", parsedMessage, previousMessages[length]);

          previousMessages[length].content = previousMessages[length].content + parsedMessage
          previousMessages[length].createdAt = new Date()
          // setMessages([...messagesRef.current.slice(0, -1), lastMessage])

          mutate([...previousMessages.slice(0, -1), ...[previousMessages[length]]], false);
        }

        break;
    }
  }






};


const getStreamedResponse = async (
  api: string | StreamingReactResponseAction,
  chatRequest: ChatRequest,
  mutate: KeyedMutator<Message[]>,
  mutateStreamData: KeyedMutator<JSONValue[] | undefined>,
  existingData: JSONValue[] | undefined,
  extraMetadataRef: React.MutableRefObject<any>,
  messagesRef: React.MutableRefObject<Message[]>,
  abortControllerRef: React.MutableRefObject<AbortController | null>,
  generateId: IdGenerator,
  onFinish?: (message: Message) => void,
  onResponse?: (response: Response) => void | Promise<void>,
  sendExtraMessageFields?: boolean,
) => {
  // Do an optimistic update to the chat state to show the updated messages
  // immediately.
  const previousMessages = messagesRef.current;
  mutate(chatRequest.messages, false);

  const constructedMessagesPayload = sendExtraMessageFields
    ? chatRequest.messages
    : chatRequest.messages.map(
      ({ role, content, name, function_call, tool_calls, tool_call_id }) => ({
        role,
        content,
        tool_call_id,
        ...(name !== undefined && { name }),
        ...(function_call !== undefined && {
          function_call: function_call,
        }),
        ...(tool_calls !== undefined && {
          tool_calls: tool_calls,
        }),
      }),
    );

  if (typeof api !== 'string') {
    // In this case, we are handling a Server Action. No complex mode handling needed.

    const replyId = generateId();
    const createdAt = new Date();
    let responseMessage: Message = {
      id: replyId,
      createdAt,
      content: '',
      role: 'assistant',
    };

    async function readRow(promise: Promise<ReactResponseRow>) {


      console.log("-----promise--------", promise)
      const { content, ui, next } = await promise;

      // TODO: Handle function calls.
      responseMessage['content'] = content;
      responseMessage['ui'] = await ui;

      mutate([...chatRequest.messages, { ...responseMessage }], false);

      if (next) {
        await readRow(next);
      }
    }

    try {
      const promise = api({
        messages: constructedMessagesPayload as Message[],
        data: chatRequest.data,
      }) as Promise<ReactResponseRow>;
      await readRow(promise);
    } catch (e) {
      // Restore the previous messages if the request fails.
      mutate(previousMessages, false);
      throw e;
    }

    if (onFinish) {
      onFinish(responseMessage);
    }

    return responseMessage;
  }

  return await callChatApi({
    api,
    messages: constructedMessagesPayload,
    body: {
      data: chatRequest.data,
      ...extraMetadataRef.current.body,
      ...chatRequest.options?.body,
      ...(chatRequest.functions !== undefined && {
        functions: chatRequest.functions,
      }),
      ...(chatRequest.function_call !== undefined && {
        function_call: chatRequest.function_call,
      }),
      ...(chatRequest.tools !== undefined && {
        tools: chatRequest.tools,
      }),
      ...(chatRequest.tool_choice !== undefined && {
        tool_choice: chatRequest.tool_choice,
      }),
    },
    credentials: extraMetadataRef.current.credentials,
    headers: {
      ...extraMetadataRef.current.headers,
      ...chatRequest.options?.headers,
    },
    abortController: () => abortControllerRef.current,
    restoreMessagesOnFailure() {
      mutate(previousMessages, false);
    },
    onResponse,
    onUpdate(merged, data) {
      console.log("------onUpdate----------", chatRequest.messages, merged, data)
      mutate([...chatRequest.messages, ...merged], false);
      mutateStreamData([...(existingData || []), ...(data || [])], false);
    },
    onFinish,
    generateId,
  });
};

export function useISDK({
  api = '/api/chat',
  id,
  initialMessages,
  initialInput = '',
  sendExtraMessageFields,
  experimental_onFunctionCall,
  experimental_onFunctionCallV2,
  experimental_onToolCall,
  onResponse,
  onFinish,
  onError,
  credentials,
  headers,
  body,
  streamMode,
  // username,
  // password,

  generateId = generateIdFunc,
}: Omit<UseChatOptions, 'api'> & {
  api?: string | StreamingReactResponseAction;
  key?: string;
} = {}): UseISDKHelpers {
  // Generate a unique id for the chat if not provided.
  const hookId = useId();
  const idKey = id ?? hookId;
  const chatKey = typeof api === 'string' ? [api, idKey] : idKey;


  const socketRef = useRef<WebSocket | null>()
  const [isSocket, setIsSocket] = useState<boolean>(false);

  // Store a empty array as the initial messages
  // (instead of using a default parameter value that gets re-created each time)
  // to avoid re-renders:
  const [initialMessagesFallback] = useState([]);

  // Store the chat state in SWR, using the chatId as the key to share states.
  const { data: messages, mutate } = useSWR<Message[]>(
    [chatKey, 'messages'],
    null,
    { fallbackData: initialMessages ?? initialMessagesFallback },
  );

  console.log("-----useSWR--initialMessages---------", initialMessages)

  // We store loading state in another hook to sync loading states across hook invocations
  const { data: isLoading = false, mutate: mutateLoading } = useSWR<boolean>(
    [chatKey, 'loading'],
    null,
  );

  const { data: streamData, mutate: mutateStreamData } = useSWR<
    JSONValue[] | undefined
  >([chatKey, 'streamData'], null);

  const { data: error = undefined, mutate: setError } = useSWR<
    undefined | Error
  >([chatKey, 'error'], null);

  const isLoadingRef = useRef<boolean>(false);
  useEffect(() => {
    isLoadingRef.current = isLoading;
  }, [isLoading]);

  // Keep the latest messages in a ref.
  const messagesRef = useRef<Message[]>(messages || []);
  useEffect(() => {
    messagesRef.current = messages || [];
  }, [messages]);

  // Abort controller to cancel the current API call.
  const abortControllerRef = useRef<AbortController | null>(null);

  const extraMetadataRef = useRef({
    credentials,
    headers,
    body,
  });

  useEffect(() => {
    extraMetadataRef.current = {
      credentials,
      headers,
      body,
    };
  }, [credentials, headers, body]);


  const setupWebSocket = ((newURL: string) => {
    if (socketRef.current) {
      socketRef.current.close(); // 关闭旧连接
    }

    // if (!!username && !!password) {
    //   // 构建 Basic 认证字符串
    //   // const credentials = Buffer.from(`${username}:${password}`).toString('base64');
    //   // const authHeader = `Basic ${credentials}`;

    //   const authHeader = `${Buffer.from(`${username}:${password}`).toString('base64')}`;

    //   const credentialsV2 = btoa(`${username}:${password}`);
    //   // socket.send('Authorization: Basic ' + credentialsV2);

    //   // socket.addEventListener('open', (event) => {
    //   //   // 在握手阶段添加Authorization头
    //   //   socket.send('Authorization: Bearer ' + authHeader);
    //   // });
    // }

    // const webSocket = new WebSocket(newURL)
    const socket = new WebSocket(newURL, []);

    // const socket = new WebSocket("ws://JS8dw07Ld0hcCsVf:8jfPM53Sq84Etvc3J98kqlxtFwqPOvVMQxlGmc3ud7sy10ch@116.213.39.234:8084/ws")

    // if (socketRef.current) return;
    // setWs(socket);

    socket.onopen = () => {
      console.log('WebSocket 连接已建立',);

      setIsSocket(true)
      if (initialInput !== undefined) {
        // console.log("---useWebSocket------", options)
        // ws.send(options.initialInput as string); // 将 undefined 转换为 string
        // setInput("")
        // append({
        //   id: '',
        //   content: initialInput,
        //   role: 'function'
        // })
      }
    };

    socket.onclose = async () => {
      console.log('WebSocket 连接已关闭', socketRef?.current);
      // if (onFinish && !!messages) {
      //   onFinish(messages);
      // }

      await new Promise(r => setTimeout(r, 3000));

      if (socketRef?.current) {
        socketRef.current.close()
        setupWebSocket(newURL)
      } else {
        setupWebSocket(newURL)
      }
    };


    socket.onerror = (error) => {
      console.error('WebSocket 连接发生错误:', error);
      if (onError) {
        // onError(error);
      }
      mutateLoading(false);// 发生错误后，设置 isLoading 为 false
    };

    let loading = false
    socket.onmessage = (event) => {
      try {
        mutateLoading(true);
        setError(undefined);

        const abortController = new AbortController();
        abortControllerRef.current = abortController;

        handleWebSocketMessage(

          event,
          messagesRef,
          isLoadingRef,
          mutate,
          setInput,
          mutateLoading,
          generateId,
          streamMode,
          onFinish,
          onResponse,
          // experimental_onFunctionCallV2: 

          async (data: WebSocketMessage) => {
            if (experimental_onFunctionCallV2) {
              await experimental_onFunctionCallV2(data)
            }

            // toolCallsResult
            // 回调成功
            if (socketRef.current) {
              // socketRef.current.send("INTENTION_CLIENT_TOOL_CALL_FUNCTION_FINISHED")
            }

          }
        );

        abortControllerRef.current = null;
      } catch (err) {
        // Ignore abort errors as they are expected.
        if ((err as any).name === 'AbortError') {
          abortControllerRef.current = null;
          return null;
        }

        if (onError && err instanceof Error) {
          onError(err);
        }

        setError(err as Error);
      } finally {
        mutateLoading(false);
      }
    }

    socketRef.current = socket

  })

  useEffect(() => {

    const socket = socketRef.current

    if (!socket) {
      // let url = "ws://116.213.39.234:8083/ws"
      // let url = "wss://www.jellyai.xyz/ws"
      let url = api as string
      setupWebSocket(url)
    }
  }, [socketRef, onResponse, onFinish, onError])

  const triggerRequest = useCallback(
    async (chatRequest: ChatRequest) => {
      try {
        mutateLoading(true);
        setError(undefined);

        const abortController = new AbortController();
        abortControllerRef.current = abortController;


        await processChatStream({
          getStreamedResponse: () =>
            getStreamedResponse(
              api,
              chatRequest,
              mutate,
              mutateStreamData,
              streamData!,
              extraMetadataRef,
              messagesRef,
              abortControllerRef,
              generateId,
              onFinish,
              onResponse,
              sendExtraMessageFields,
            ),
          experimental_onFunctionCall,
          experimental_onToolCall,
          updateChatRequest: chatRequestParam => {
            chatRequest = chatRequestParam;
          },
          getCurrentMessages: () => messagesRef.current,
        });

        abortControllerRef.current = null;
      } catch (err) {
        // Ignore abort errors as they are expected.
        if ((err as any).name === 'AbortError') {
          abortControllerRef.current = null;
          return null;
        }

        if (onError && err instanceof Error) {
          onError(err);
        }

        setError(err as Error);
      } finally {
        mutateLoading(false);
      }
    },
    [
      mutate,
      mutateLoading,
      api,
      extraMetadataRef,
      onResponse,
      onFinish,
      onError,
      setError,
      mutateStreamData,
      streamData,
      sendExtraMessageFields,
      experimental_onFunctionCall,
      experimental_onToolCall,
      messagesRef,
      abortControllerRef,
      generateId,
    ],
  );

  const append = useCallback(
    async (
      message: Message | CreateMessage,
      {
        options,
        functions,
        function_call,
        tools,
        tool_choice,
        data,
      }: ChatRequestOptions = {},
    ) => {
      if (!message.id) {
        message.id = generateId();
      }

      const chatRequest: ChatRequest = {
        messages: messagesRef.current.concat(message as Message),
        options,
        data,
        ...(functions !== undefined && { functions }),
        ...(function_call !== undefined && { function_call }),
        ...(tools !== undefined && { tools }),
        ...(tool_choice !== undefined && { tool_choice }),
      };

      if (isSocket) {
        try {
          if (socketRef?.current && socketRef?.current.readyState === WebSocket.OPEN) {

            // setMessages([...messagesRef.current, {
            //   id: message.id,
            //   content: message.content,
            //   role: message.role
            // }])
            mutateLoading(true);

            mutate([...messagesRef.current, {
              id: message.id,
              content: message.content,
              role: message.role
            }], false)

            // 发送消息时，设置 isLoading 为 true
            // socketRef.current.send(message.content);
            socketRef.current.send(JSON.stringify(message));
          } else {
            console.error('WebSocket 连接未建立或已关闭');
          }
        } catch (error) {

        } finally {
          mutateLoading(false);
        }
      } else {
        return triggerRequest(chatRequest);
      }

      // return triggerRequest(chatRequest);

    },
    [triggerRequest, generateId],
  );

  const reload = useCallback(
    async ({
      options,
      functions,
      function_call,
      tools,
      tool_choice,
    }: ChatRequestOptions = {}) => {
      if (messagesRef.current.length === 0) return null;

      // if (isSocket) {
      //   try {
      //     if (socketRef?.current && socketRef?.current.readyState === WebSocket.OPEN) {
      //       mutateLoading(true); // 发送消息时，设置 isLoading 为 true
      //       socketRef.current.send(message.content);
      //     } else {
      //       console.error('WebSocket 连接未建立或已关闭');
      //     }
      //   } catch (error) {

      //   } finally {
      //     mutateLoading(false);
      //   }

      //   return
      // } 


      // Remove last assistant message and retry last user message.
      const lastMessage = messagesRef.current[messagesRef.current.length - 1];
      if (lastMessage.role === 'assistant') {
        const chatRequest: ChatRequest = {
          messages: messagesRef.current.slice(0, -1),
          options,
          ...(functions !== undefined && { functions }),
          ...(function_call !== undefined && { function_call }),
          ...(tools !== undefined && { tools }),
          ...(tool_choice !== undefined && { tool_choice }),
        };

        return triggerRequest(chatRequest);
      }

      const chatRequest: ChatRequest = {
        messages: messagesRef.current,
        options,
        ...(functions !== undefined && { functions }),
        ...(function_call !== undefined && { function_call }),
        ...(tools !== undefined && { tools }),
        ...(tool_choice !== undefined && { tool_choice }),
      };

      return triggerRequest(chatRequest);
    },
    [triggerRequest],
  );

  const stop = useCallback(() => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
      abortControllerRef.current = null;
    }
  }, []);

  const setMessages = useCallback(
    (messages: Message[]) => {
      mutate(messages, false);
      messagesRef.current = messages;
    },
    [mutate],
  );

  // Input state and handlers.
  const [input, setInput] = useState(initialInput);

  const handleSubmit = useCallback(
    (
      e: React.FormEvent<HTMLFormElement>,
      options: ChatRequestOptions = {},
      metadata?: Object,
    ) => {
      if (metadata) {
        extraMetadataRef.current = {
          ...extraMetadataRef.current,
          ...metadata,
        };
      }

      e.preventDefault();
      if (!input) return;

      append(
        {
          content: input,
          role: 'user',
          createdAt: new Date(),
          action: "chat",
          type: 'text',
        },
        options,
      );
      setInput('');
    },
    [input, append],
  );

  const handleInputChange = (e: any) => {
    setInput(e.target.value);
  };

  return {
    messages: messages || [],
    error,
    append,
    reload,
    stop,
    setMessages,
    input,
    setInput,
    handleInputChange,
    handleSubmit,
    isLoading,
    isSocket,
    data: streamData,
  };
}
