// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
#cfg(not(target="js"))
fn join_length_add(length : Int32, extra : Int32) -> Int32 {
  guard extra <= 0x7FFFFFFF - length else {
    abort("String join length overflow")
  }
  length + extra
}

///|
#cfg(not(target="js"))
fn[A : ToStringView] join_string_views(
  parts : ArrayView[A],
  separator : StringView,
) -> String {
  match parts {
    [] => ""
    [one] => one.to_string_view().to_owned()
    [first, .. rest] => {
      // ToStringView is closed to external implementations; String and
      // StringView conversions are stable, so two passes need no cache.
      let first = first.to_string_view()
      let length = for part in rest; length = first.length() {
        continue join_length_add(
            join_length_add(length, separator.length()),
            part.to_string_view().length(),
          )
      } nobreak {
        length
      }
      if length == 0 {
        return ""
      }
      let output : UninitializedArray[UInt16] = UninitializedArray::make(length)
      output.unsafe_blit_from_string_uninit(
        0,
        first.data(),
        first.start_offset(),
        first.length(),
      )
      for part in rest; offset = first.length() {
        let part = part.to_string_view()
        output.unsafe_blit_from_string_uninit(
          offset,
          separator.data(),
          separator.start_offset(),
          separator.length(),
        )
        let offset = offset + separator.length()
        output.unsafe_blit_from_string_uninit(
          offset,
          part.data(),
          part.start_offset(),
          part.length(),
        )
        continue offset + part.length()
      }
      // All `length` slots have been initialized exactly once. The output
      // buffer is local and will never be mutated after conversion.
      unsafe_fixedarray_uint16_to_string(
        unsafe_uint16_buffer_as_fixedarray(output),
      )
    }
  }
}

///|
#cfg(target="js")
fn[A : ToStringView] join_string_views(
  parts : ArrayView[A],
  separator : StringView,
) -> String {
  match parts {
    [] => ""
    [one] => one.to_string_view().to_owned()
    [first, .. rest] => {
      let separator = separator.to_owned()
      let mut result = first.to_string_view().to_owned()
      for part in rest {
        result += separator
        result += part.to_string_view().to_owned()
      }
      result
    }
  }
}