using System.Text;

namespace SharedKernel.Helpers;

/// <summary>
/// RFC 4180 호환 CSV 직렬화/역직렬화. 외부 라이브러리 미사용 (bitforum 코드 컨벤션).
/// 직렬화는 UTF-8 BOM 포함 (Excel 호환).
/// </summary>
public static class CsvHelper
{
    /// <summary>
    /// 행 시퀀스를 RFC 4180 CSV byte[] 로 직렬화. UTF-8 BOM 포함.
    /// </summary>
    public static byte[] WriteUtf8Bom(IEnumerable<string[]> rows)
    {
        var sb = new StringBuilder();
        foreach (var row in rows)
        {
            for (var i = 0; i < row.Length; i++)
            {
                if (i > 0)
                {
                    sb.Append(',');
                }
                sb.Append(EscapeCell(row[i]));
            }
            sb.Append("\r\n");
        }

        var data = Encoding.UTF8.GetBytes(sb.ToString());
        var bom = new byte[] { 0xEF, 0xBB, 0xBF };
        var result = new byte[bom.Length + data.Length];
        Buffer.BlockCopy(bom, 0, result, 0, bom.Length);
        Buffer.BlockCopy(data, 0, result, bom.Length, data.Length);
        return result;
    }

    /// <summary>RFC 4180 escape: 쉼표/따옴표/개행 포함 시 "..." 로 감싸고 내부 따옴표는 "" 로.</summary>
    public static string EscapeCell(string? cell)
    {
        cell ??= "";

        if (cell.Contains(',') || cell.Contains('"') || cell.Contains('\n') || cell.Contains('\r'))
        {
            return "\"" + cell.Replace("\"", "\"\"") + "\"";
        }

        return cell;
    }

    /// <summary>
    /// TextReader 에서 RFC 4180 CSV 행을 lazy 하게 읽는다.
    /// UTF-8 BOM 이 앞에 있으면 자동으로 건너뜀 (StreamReader 가 처리해 주거나, 여기서 fallback).
    /// </summary>
    public static IEnumerable<string[]> Read(TextReader reader)
    {
        var currentRow = new List<string>();
        var currentField = new StringBuilder();
        var inQuotes = false;
        var hasContent = false;
        var atStart = true;
        int ch;

        while ((ch = reader.Read()) >= 0)
        {
            var c = (char)ch;

            // 파일 첫머리 BOM (U+FEFF) 건너뜀 (StreamReader 가 못 처리한 경우 대비)
            if (atStart)
            {
                atStart = false;
                if (c == '﻿')
                {
                    continue;
                }
            }

            if (inQuotes)
            {
                if (c == '"')
                {
                    var next = reader.Peek();

                    if (next == '"')
                    {
                        currentField.Append('"');
                        reader.Read();
                    }
                    else
                    {
                        inQuotes = false;
                    }
                }
                else
                {
                    currentField.Append(c);
                }

                hasContent = true;
            }
            else
            {
                if (c == '"')
                {
                    inQuotes = true;
                    hasContent = true;
                }
                else if (c == ',')
                {
                    currentRow.Add(currentField.ToString());
                    currentField.Clear();
                    hasContent = true;
                }
                else if (c == '\r')
                {
                    // \r\n 정상 처리: \n 이 뒤에 오면 그때 행 종료
                }
                else if (c == '\n')
                {
                    if (hasContent || currentRow.Count > 0)
                    {
                        currentRow.Add(currentField.ToString());
                        currentField.Clear();
                        yield return currentRow.ToArray();
                        currentRow = new List<string>();
                        hasContent = false;
                    }
                }
                else
                {
                    currentField.Append(c);
                    hasContent = true;
                }
            }
        }

        // 마지막 행: 개행 없이 끝난 경우
        if (hasContent || currentRow.Count > 0)
        {
            currentRow.Add(currentField.ToString());
            yield return currentRow.ToArray();
        }
    }
}
