using System.Globalization;
using System.Net;
using System.Text;

namespace BackOffice.Common.Utilities;

/// <summary>
/// خروجی سازگار با Excel فارسی:
/// - XML Spreadsheet (.xls) برای ستون‌بندی مطمئن + متن فارسی
/// - CSV با جداکننده ; به‌عنوان جایگزین سبک
/// </summary>
public static class CsvExportHelper
{
    /// <summary>جداکننده پیش‌فرض ویندوز فارسی برای CSV.</summary>
    public const char Separator = ';';

    public static string Escape(string? value)
    {
        if (string.IsNullOrEmpty(value))
            return string.Empty;

        // همیشه quote تا Excel فارسی ستون را نشکند
        return $"\"{value.Replace("\"", "\"\"")}\"";
    }

    public static string Cell(object? value) => value switch
    {
        null => Escape(string.Empty),
        string s => Escape(s),
        bool b => b ? "1" : "0",
        byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
            => Convert.ToString(value, CultureInfo.InvariantCulture) ?? "0",
        IFormattable f => Escape(f.ToString(null, CultureInfo.InvariantCulture) ?? string.Empty),
        _ => Escape(Convert.ToString(value, CultureInfo.InvariantCulture))
    };

    public static string Row(params object?[] cells) =>
        string.Join(Separator, cells.Select(Cell));

    /// <summary>
    /// نام نمایشی مشتری؛ اگر خالی باشد موبایل یا شناسه کاربر.
    /// </summary>
    public static string CustomerName(string? fullName, string? mobile = null, long? userId = null)
    {
        if (!string.IsNullOrWhiteSpace(fullName))
            return fullName.Trim();
        if (!string.IsNullOrWhiteSpace(mobile))
            return mobile.Trim();
        if (userId is > 0)
            return $"کاربر {userId}";
        return "—";
    }

    public static string ToBase64(StringBuilder body)
    {
        // CRLF صریح — Excel روی ویندوز با \n لینوکس گاهی به‌هم می‌ریزد
        var text = body.ToString().Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n");
        var bytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(text)).ToArray();
        return Convert.ToBase64String(bytes);
    }

    /// <summary>
    /// خروجی SpreadsheetML که Excel به‌صورت ستون‌ستون و با UTF-8 درست باز می‌کند.
    /// فایل را با پسوند .xls ذخیره کنید.
    /// </summary>
    public static string ToExcelXmlBase64(string sheetName, IReadOnlyList<string> headers, IEnumerable<object?[]> rows)
    {
        var sb = new StringBuilder();
        sb.AppendLine(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
        sb.AppendLine(@"<?mso-application progid=""Excel.Sheet""?>");
        sb.AppendLine(@"<Workbook xmlns=""urn:schemas-microsoft-com:office:spreadsheet""");
        sb.AppendLine(@" xmlns:o=""urn:schemas-microsoft-com:office:office""");
        sb.AppendLine(@" xmlns:x=""urn:schemas-microsoft-com:office:excel""");
        sb.AppendLine(@" xmlns:ss=""urn:schemas-microsoft-com:office:spreadsheet""");
        sb.AppendLine(@" xmlns:html=""http://www.w3.org/TR/REC-html40"">");
        sb.AppendLine(@"<Styles>");
        sb.AppendLine(@"<Style ss:ID=""Header""><Font ss:Bold=""1""/></Style>");
        sb.AppendLine(@"<Style ss:ID=""Text""><NumberFormat ss:Format=""@""/></Style>");
        sb.AppendLine(@"</Styles>");
        sb.Append("<Worksheet ss:Name=\"").Append(XmlEscape(sheetName)).AppendLine("\">");
        sb.AppendLine("<Table>");

        sb.AppendLine("<Row>");
        foreach (var header in headers)
        {
            sb.Append("<Cell ss:StyleID=\"Header\"><Data ss:Type=\"String\">")
              .Append(XmlEscape(header))
              .AppendLine("</Data></Cell>");
        }
        sb.AppendLine("</Row>");

        foreach (var row in rows)
        {
            sb.AppendLine("<Row>");
            foreach (var cell in row)
                AppendExcelCell(sb, cell);
            sb.AppendLine("</Row>");
        }

        sb.AppendLine("</Table>");
        sb.AppendLine("</Worksheet>");
        sb.AppendLine("</Workbook>");

        var bytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(sb.ToString())).ToArray();
        return Convert.ToBase64String(bytes);
    }

    private static void AppendExcelCell(StringBuilder sb, object? value)
    {
        switch (value)
        {
            case null:
                sb.AppendLine(@"<Cell ss:StyleID=""Text""><Data ss:Type=""String""></Data></Cell>");
                break;
            case string s:
                sb.Append(@"<Cell ss:StyleID=""Text""><Data ss:Type=""String"">")
                  .Append(XmlEscape(s))
                  .AppendLine("</Data></Cell>");
                break;
            case bool b:
                sb.Append(@"<Cell><Data ss:Type=""Boolean"">")
                  .Append(b ? "1" : "0")
                  .AppendLine("</Data></Cell>");
                break;
            case byte or sbyte or short or ushort or int or uint or long or ulong:
                sb.Append(@"<Cell><Data ss:Type=""Number"">")
                  .Append(Convert.ToString(value, CultureInfo.InvariantCulture))
                  .AppendLine("</Data></Cell>");
                break;
            case float or double or decimal:
                sb.Append(@"<Cell><Data ss:Type=""Number"">")
                  .Append(Convert.ToString(value, CultureInfo.InvariantCulture))
                  .AppendLine("</Data></Cell>");
                break;
            default:
                sb.Append(@"<Cell ss:StyleID=""Text""><Data ss:Type=""String"">")
                  .Append(XmlEscape(Convert.ToString(value, CultureInfo.InvariantCulture)))
                  .AppendLine("</Data></Cell>");
                break;
        }
    }

    private static string XmlEscape(string? value) =>
        WebUtility.HtmlEncode(value ?? string.Empty);
}
