///|
/// Represents a CSV (Comma-Separated Values) data structure with headers and
/// rows.
///
/// Parameters:
///
/// * `headers` : An array of strings representing the column names in the CSV.
/// * `rows` : A two-dimensional array where each inner array represents a row of
/// data, with each element corresponding to a column defined in the headers.
///
struct CSV {
  headers : Array[String]
  rows : Array[Array[String]]
}

///|
/// header of the CSV.
pub fn CSV::header(self : CSV) -> Array[String] {
  self.headers
}

///|
/// data of the CSV.
pub fn CSV::data(self : CSV) -> Array[Array[String]] {
  self.rows
}

///|
/// shape of the CSV.
pub fn CSV::shape(self : CSV) -> (Int, Int) {
  let row_count = self.rows.length()
  let column_count = if self.headers.length() > 0 {
    self.headers.length()
  } else if row_count > 0 {
    self.rows[0].length()
  } else {
    0
  }
  (row_count, column_count)
}

///|
fn draw_border(logger : &Logger, column_widths : Array[Int]) -> Unit {
  logger.write_string("+")
  for width in column_widths {
    logger.write_string("-".repeat(width + 2))
    logger.write_string("+")
  }
  logger.write_string("\n")
}

///|
pub impl Show for CSV with output(self, logger) -> Unit {
  if self.headers.is_empty() {
    logger.write_string("")
  } else {
    let column_widths : Array[Int] = Array::make(self.headers.length(), 0)

    // 计算表头宽度
    for i in 0.. String {
  if value.contains_any(chars=",\"\n\r") {
    let escaped = value.replace_all(old="\"", new="\"\"")
    let result = StringBuilder::new()
    result.write_char('"')
    result.write_string(escaped)
    result.write_char('"')
    result.to_string()
  } else {
    value
  }
}

///|
fn write_csv_row(result : StringBuilder, row : Array[String]) -> Unit {
  for i in 0.. 0 {
      result.write_char(',')
    }
    result.write_string(escape_csv_field(row[i]))
  }
  result.write_char('\n')
}

///|
pub impl Show for CSV with to_string(self) -> String {
  let result = StringBuilder::new()

  // 添加表头
  write_csv_row(result, self.headers)

  // 添加数据行
  for row in self.rows {
    write_csv_row(result, row)
  }
  result.to_string()
}

///|
test "CSV::to_string/single_header_no_rows" {
  let csv = { headers: ["Name"], rows: [] }
  let expected =
    #|Name
    #|
  inspect(csv.to_string(), content=expected)
}

///|
test "CSV::to_string/headers_and_rows" {
  let csv = {
    headers: ["Name", "Age", "City"],
    rows: [["John", "25", "New York"], ["Jane", "30", "San Francisco"]],
  }
  let expected =
    #|Name,Age,City
    #|John,25,New York
    #|Jane,30,San Francisco
    #|
  inspect(csv.to_string(), content=expected)
}

///|
/// Creates a new empty CSV structure with no headers and no rows.
///
/// Returns a new CSV instance with empty headers and rows arrays.
///
pub fn CSV::new() -> CSV {
  let headers : Array[String] = Array::new()
  let rows : Array[Array[String]] = Array::new()
  { headers, rows }
}

///|
pub fn CSV::from_array(
  data : Array[Array[String]],
  has_header? : Bool = true,
  generate_headers? : Bool = true,
) -> CSV {
  if data.is_empty() {
    CSV::new()
  } else if has_header {
    let headers = data[0]
    let rows = data[1:data.length()].iter().collect()
    { headers, rows }
  } else if generate_headers {
    let column_count = data[0].length()
    let headers = Array::makei(column_count, fn(i) { "column\{i+1}" })
    { headers, rows: data }
  } else {
    let column_count = data[0].length()
    let headers = Array::make(column_count, "")
    { headers, rows: data }
  }
}

///|
/// Parses a CSV (Comma-Separated Values) string into a structured CSV object.
///
/// Parameters:
///
/// * `csv_string` : A string containing CSV data to be parsed.
/// * `options` : A configuration object of type `CSVOptions` that controls the
/// parsing behavior with the following fields:
///  * `delimiter` : The character used to separate fields (default: ',')
///  * `allow_newlines_in_quotes` : Whether to allow newlines within quoted
/// fields (default: true)
///  * `quote_char` : The character used for quoting fields (default: '"')
///  * `skip_empty_lines` : Whether to ignore empty lines in the input (default:
/// true)
///  * `trim_spaces` : Whether to remove leading and trailing whitespace from
/// fields (default: false)
///
/// Returns a `CSV` object containing the parsed data, with headers from the
/// first row and subsequent rows as data.
pub fn CSV::parse_string(
  data : String,
  options? : CSVOptions = {
    delimiter: ',',
    allow_newlines_in_quotes: true,
    quote_char: '"',
    skip_empty_lines: true,
    trim_spaces: false,
  },
) -> CSV {
  let reader : StringReader = { source: data, pos: 0 }
  CSV::from_array(parse(reader, options~))
}

///|
test "CSV::parse_buffer/basic" {
  let buffer = @buffer.new()
  let data =
    #|header1,header2
    #|value1,value2
    #|
  buffer.write_string(data)
  let csv = CSV::parse_buffer(buffer)
  inspect(csv.header(), content="[\"header1\", \"header2\"]")
  inspect(csv.data(), content="[[\"value1\", \"value2\"]]")
}

///|
/// Parses a buffer containing CSV data into a structured CSV object.
///
/// Parameters:
///
/// * `buffer` : A buffer containing the CSV data to be parsed.
/// * `options` : A configuration object of type `CSVOptions` that controls
/// parsing behavior with the following fields:
///  * `delimiter` : The character used to separate fields (default: ',')
///  * `allow_newlines_in_quotes` : Whether to allow newlines within quoted
/// fields (default: true)
///  * `quote_char` : The character used for quoting fields (default: '"')
///  * `skip_empty_lines` : Whether to ignore empty lines in the input (default:
/// true)
///  * `trim_spaces` : Whether to remove leading and trailing whitespace from
/// fields (default: false)
///
/// Returns a `CSV` object containing the parsed data, with headers from the
/// first row and subsequent rows as data.
pub fn CSV::parse_buffer(
  data : @buffer.Buffer,
  options? : CSVOptions = {
    delimiter: ',',
    allow_newlines_in_quotes: true,
    quote_char: '"',
    skip_empty_lines: true,
    trim_spaces: false,
  },
) -> CSV {
  let reader : StringReader = {
    source: data.contents().to_unchecked_string(),
    pos: 0,
  }
  CSV::from_array(parse(reader, options~))
}

///|
/// Parses binary data containing CSV content into a structured CSV object.
///
/// Parameters:
///
/// * `data` : A sequence of bytes containing the CSV content to be parsed.
/// * `options` : A configuration object that controls parsing behavior with the
/// following fields:
///  * `delimiter` : The character used to separate fields (default: ',')
///  * `allow_newlines_in_quotes` : Whether to allow newlines within quoted
/// fields (default: true)
///  * `quote_char` : The character used for quoting fields (default: '"')
///  * `skip_empty_lines` : Whether to ignore empty lines in the input (default:
/// true)
///  * `trim_spaces` : Whether to remove leading and trailing whitespace from
/// fields (default: false)
///
/// Returns a `CSV` object containing the parsed data, with headers from the
/// first row and subsequent rows as data.
pub fn CSV::parse_bytes(
  data : Bytes,
  options? : CSVOptions = {
    delimiter: ',',
    allow_newlines_in_quotes: true,
    quote_char: '"',
    skip_empty_lines: true,
    trim_spaces: false,
  },
) -> CSV {
  let source = @encoding/utf8.decode_lossy(data)
  let reader : StringReader = { source, pos: 0 }
  CSV::from_array(parse(reader, options~))
}

///|
test "CSV::parse_bytes/basic_csv" {
  let source =
    #|header1,header2
    #|value1,value2
    #|
  let data = @encoding/utf8.encode(source)
  let csv = CSV::parse_bytes(data)
  inspect(csv.header(), content="[\"header1\", \"header2\"]")
  inspect(csv.data(), content="[[\"value1\", \"value2\"]]")
}