///|
/// Provider adapter session backed by an OpenCode SDK thread.
priv struct OpenCodeCliSession {
  client : @sdk.Client
  thread : @sdk.Thread
  thread_options : @sdk.ThreadOptions
}

///|
impl @cli.CliSession for OpenCodeCliSession with fn id(self) {
  self.thread.id()
}

///|
impl @cli.CliSession for OpenCodeCliSession with fn continuation(self) {
  self.thread
  .id()
  .map(fn(continuation_id) {
    @cli.Continuation::Continuation(fn() {
      opencode_session(self.client, self.thread_options, Some(continuation_id))
    })
  })
}

///|
impl @cli.CliSession for OpenCodeCliSession with fn prompt(self, prompt) {
  let turn = self.thread.run(opencode_input(prompt))
  let continuation = @cli.CliSession::continuation(self)
  @cli.FinalResponse::FinalResponse(
    turn.final_response,
    session_id?=self.thread.id(),
    changed_files=[],
    continuation?,
  )
}

///|
fn opencode_input(prompt : @cli.Prompt) -> @sdk.Input {
  if prompt.context_files.is_empty() {
    return @sdk.Prompt(prompt.text)
  }
  let inputs : Array[@sdk.UserInput] = [@sdk.Text(prompt.text)]
  for path in prompt.context_files {
    inputs.push(@sdk.LocalFile(path))
  }
  @sdk.UserInputs(inputs)
}

///|
fn opencode_session(
  client : @sdk.Client,
  thread_options : @sdk.ThreadOptions,
  continuation_id : String?,
) -> &@cli.CliSession {
  let thread = match continuation_id {
    Some(id) => client.resume_thread(id, options=thread_options)
    None => client.start_thread(options=thread_options)
  }
  let session = OpenCodeCliSession::{ client, thread, thread_options }
  (session : &@cli.CliSession)
}

///|
/// Creates an OpenCode-backed provider-neutral CLI adapter.
///
/// OpenCode-native client and thread options remain available at this package
/// boundary. Prompt context paths are forwarded as ordered local-file inputs;
/// OpenCode currently returns an empty `FinalResponse.changed_files` collection
/// because its event model does not expose a reliable change set.
///
/// ```check
/// let cli = opencode_cli()
/// let session = cli.start()
/// assert_eq(session.id(), None)
/// ```
pub fn opencode_cli(
  options? : @sdk.ClientOptions = @sdk.ClientOptions::ClientOptions(),
  thread_options? : @sdk.ThreadOptions = @sdk.ThreadOptions::ThreadOptions(),
  resume_thread_id? : String,
) -> @cli.Cli {
  let client = @sdk.Client::Client(options~)
  @cli.Cli::Cli(fn() {
    opencode_session(client, thread_options, resume_thread_id)
  })
}