///|
/// `suspend` 会中断当前协程的运行。
/// `suspend` 会接受一个回调函数,并让这个回调函数来操作中断的协程
async fn[T, E : Error] suspend(
  // `f` 是负责操作中断的协程的回调函数
  f : (
    // `f` 的第一个参数用于继续运行被中断的协程
    (T) -> Unit,
    // `f` 的第二个参数用于取消被中断的协程。
    // 取消会被表示为在中断处抛出错误
    (E) -> Unit,
  ) -> Unit,
) -> T raise E = "%async.suspend"

///|
async fn request_internal_ffi(
  args : String,
) -> (String, String, Bytes) raise NetworkError {
  suspend(fn(resume_ok, resume_err) {
    request_internal(
      args,
      fn(info, headers, data) {
        resume_ok((info, headers, Bytes::from_array(data)))
      },
      resume_err,
    )
  }) catch {
    _ => raise NetworkError
  }
}

///|
async fn request_stream_internal_ffi(
  args : String,
  on_chunk : (String) -> Unit,
) -> String raise NetworkError {
  suspend(fn(resume_ok, resume_err) {
    request_stream_internal(args, on_chunk, resume_ok, resume_err)
  }) catch {
    _ => raise NetworkError
  }
}

///|
extern "js" fn request_internal(
  args : String,
  resume_ok : (String, String, Array[Byte]) -> Unit,
  resume_err : (Error) -> Unit,
) -> Unit =
  #|async function(args, resume_ok, resume_err) {
  #|  const [url, options] = JSON.parse(args);
  #|  try {
  #|    const res = await fetch(url, options)
  #|    const buffer = await res.arrayBuffer()
  #|    const headersObj = {};
  #|    res.headers.forEach((value, name) => {
  #|      headersObj[name] = value;
  #|    });
  #|    const responseInfo = JSON.stringify({
  #|      "status": res.status,
  #|      "statusText": res.statusText,
  #|      "ok": res.ok
  #|    });
  #|    const headersJson = JSON.stringify(headersObj);
  #|    const dataArray = Array.from(new Uint8Array(buffer));
  #|    resume_ok(responseInfo, headersJson, dataArray);
  #|  } catch (error) {
  #|    resume_err(error)
  #|  }
  #|}

///|
extern "js" fn request_stream_internal(
  args : String,
  on_chunk : (String) -> Unit,
  resume_ok : (String) -> Unit,
  resume_err : (Error) -> Unit,
) -> Unit =
  #|async function(args, on_chunk, resume_ok, resume_err) {
  #|  const [url, options] = JSON.parse(args);
  #|  try {
  #|    const res = await fetch(url, options);
  #|    const headersObj = {};
  #|    res.headers.forEach((value, name) => {
  #|      headersObj[name] = value;
  #|    });
  #|    
  #|    if (res.body && res.body.getReader) {
  #|      const reader = res.body.getReader();
  #|      const decoder = new TextDecoder();
  #|      let buffer = '';
  #|      
  #|      try {
  #|        while (true) {
  #|          const { done, value } = await reader.read();
  #|          if (done) break;
  #|          
  #|          buffer += decoder.decode(value, { stream: true });
  #|          
  #|          // Process complete lines
  #|          let lines = buffer.split('\n');
  #|          buffer = lines.pop() || ''; // Keep incomplete line in buffer
  #|          
  #|          for (const line of lines) {
  #|            if (line.trim()) {
  #|              on_chunk(line + '\n');
  #|            }
  #|          }
  #|        }
  #|        
  #|        // Process any remaining buffer
  #|        if (buffer.trim()) {
  #|          on_chunk(buffer);
  #|        }
  #|        
  #|        resume_ok(
  #|          JSON.stringify({
  #|            "headers": headersObj,
  #|            "status": res.status,
  #|            "statusText": res.statusText,
  #|            "ok": res.ok,
  #|            "data": "stream_completed"
  #|          })
  #|        );
  #|      } catch (streamError) {
  #|        resume_err(streamError);
  #|      }
  #|    } else {
  #|      // Fallback to regular text response
  #|      const text = await res.text();
  #|      on_chunk(text);
  #|      resume_ok(
  #|        JSON.stringify({
  #|          "headers": headersObj,
  #|          "status": res.status,
  #|          "statusText": res.statusText,
  #|          "ok": res.ok,
  #|          "data": text
  #|        })
  #|      );
  #|    }
  #|  } catch (error) {
  #|    resume_err(error);
  #|  }
  #|}