using Newtonsoft.Json.Linq;
using System.Collections;
using System.Data;
using System.Net;
using System.Text;
using System.Text.Json.Nodes;
namespace Tango.Portal.Chat.Web.Utils
{
public static class DataHelper
{
///
/// Converts a DataTable into a robust Markdown table string.
/// - HTML-encodes (&, <, >) to avoid HTML injection.
/// - Replaces '|' with | so it doesn't break cell boundaries.
/// - Normalizes CR/LF and turns newlines into so multi-line cells stay in-row.
/// - Replaces tabs with spaces.
/// - Optionally truncates very long cells.
///
public static string ToMarkdownTable(DataTable table, int maxCellChars = 100)
{
if (table == null || table.Columns.Count == 0) return string.Empty;
string Esc(string? s)
{
if (string.IsNullOrEmpty(s)) return string.Empty;
// HTML-encode first (safer in Markdown renderers that allow HTML)
string t = WebUtility.HtmlEncode(s);
// Normalize newlines and tabs
t = t.Replace("\r\n", "\n").Replace("\r", "\n");
t = t.Replace("\t", " ");
t = t.Replace("\n", " ");
// Escape Markdown table pipes via HTML entity (more reliable than backslash)
t = t.Replace("|", "|");
if (maxCellChars > 0 && t.Length > maxCellChars)
t = t.Substring(0, maxCellChars) + "…";
return t;
}
var sb = new StringBuilder();
// Header row
for (int c = 0; c < table.Columns.Count; c++)
sb.Append("| ").Append(Esc(table.Columns[c].ColumnName)).Append(' ');
sb.AppendLine("|");
// Separator row
for (int c = 0; c < table.Columns.Count; c++)
sb.Append("|---");
sb.AppendLine("|");
// Data rows
foreach (DataRow row in table.Rows)
{
for (int c = 0; c < table.Columns.Count; c++)
sb.Append("| ").Append(Esc(row[c]?.ToString())).Append(' ');
sb.AppendLine("|");
}
return sb.ToString();
}
public static object ToPreview(DataTable dt, int maxRows)
{
var cols = dt.Columns.Cast().Select(c => c.ColumnName).ToArray();
var rows = new List>();
int count = 0;
foreach (DataRow r in dt.Rows)
{
if (count++ >= maxRows) break;
var d = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var c in cols)
{
var cell = r[c];
d[c] = NormalizeCell(cell);
}
rows.Add(d);
}
// (Optional but helpful) include the full row count
return new { columns = cols, rows, totalRows = dt.Rows.Count };
}
private static object? NormalizeCell(object? v)
{
if (v is null || v is DBNull) return null;
// Newtonsoft JToken -> System.Text.Json node
if (v is JToken jt)
{
// Preserves arrays/objects rather than stringifying
return JsonNode.Parse(jt.ToString(Newtonsoft.Json.Formatting.None));
}
// Parse JSON strings (dynamic often arrives as text)
if (v is string s)
{
s = s.Trim();
if ((s.StartsWith("[") && s.EndsWith("]")) || (s.StartsWith("{") && s.EndsWith("}")))
{
try { return JsonNode.Parse(s); } catch { /* fall through */ }
}
return s; // plain string
}
// Flatten enumerables (but not strings)
if (v is IEnumerable en && v is not string)
{
var list = new List