# CSV/TSV Implementation in mq
# Based on RFC 4180 for CSV format

def csv_needs_quote(field, delimiter):
  let s = to_string(field)
  | contains(s, "\"") || contains(s, "\n") || contains(s, "\r") || contains(s, delimiter) || starts_with(s, " ") || ends_with(s, " ")
end

# Parses CSV content with a specified delimiter and optional header row.
def csv_parse_with_delimiter(input, delimiter, has_header): _csv_parse(input, delimiter, has_header);

# Parses CSV content using a comma as the delimiter.
def csv_parse(input, has_header): _csv_parse(input, ",", has_header);

# Parses TSV (Tab-Separated Values) content.
def tsv_parse(input, has_header): _csv_parse(input, "\t", has_header);

# Parses PSV (Pipe-Separated Values) content.
def psv_parse(input, has_header): _csv_parse(input, "|", has_header);

# Converts data to a CSV string with a specified delimiter.
def csv_stringify(data, delimiter):
  let headers = if (is_array(data) && len(data) > 0 && is_dict(first(data))):
    keys(first(data))
  else: first(data)
  | let header_line = join(headers, delimiter)
  | let data = if (is_dict(first(data))): data else: data[1:len(data)]
  | let process_row = fn(row):
      if (is_dict(row)):
        join(map(headers, fn(header):
          if (row[header]):
            if (csv_needs_quote(row[header], delimiter)):
              "\"" + to_string(row[header]) + "\""
            else:
              to_string(row[header])
          else:
            "";), delimiter)
      else:
        join(map(row, fn(field):
          let s = to_string(field)
          | if (csv_needs_quote(field, delimiter)):
              s"\"${s}\""
            else: s;), delimiter)
    end
  | let data_lines = map(data, process_row)
  | [header_line] + data_lines
  | join("\n")
end

# Converts CSV data to a Markdown table format.
def csv_to_markdown_table(data):
  let headers = if (is_array(data) && len(data) > 0 && is_dict(first(data))):
    keys(first(data))
  else: first(data)
  | let header_row = "| " + join(headers, " | ") + " |"
  | let separator_row = "| " + join(map(headers, fn(_): "---";), " | ") + " |"
  | let data = if (is_dict(first(data))): data else: data[1:len(data)]
  | let data_rows = map(data, fn(row):
      if (is_dict(row)):
        "| " + join(map(headers, fn(header): if (row[header]): to_string(row[header]) else: "";), " | ") + " |"
      else:
        "| " + join(map(row, to_string), " | ") + " |"
    end)
  | [header_row, separator_row] + data_rows
  | join("\n")
end

# Converts CSV data to a JSON string.
def csv_to_json(data):
  def _to_json(value):
    if (is_dict(value)):
      "{" + join(map(keys(value), fn(k): "\"" + k + "\":" + _to_json(value[k]);), ",") + "}"
    elif (is_array(value)):
      "[" + join(map(value, _to_json), ",") + "]"
    elif (is_string(value)):
      "\"" + replace(replace(value, "\"", "\\\""), "\n", "\\n") + "\""
    elif (is_number(value)):
      to_string(value)
    elif (is_bool(value)):
      if (value): "true" else: "false"
    elif (is_none(value)):
      "null"
    else:
      "\"" + to_string(value) + "\""
  end

  | if (is_array(data) && len(data) > 0 && is_dict(first(data))):
      _to_json(data)
    else:
      _to_json(map(data, fn(row): row;))
end
