using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Elwig.Helpers.Export {
    public abstract class Csv<T> : IExporter<T> {

        public static string FileExtension => "csv";

        private readonly StreamWriter _writer;
        protected readonly char Separator;
        protected string? Header;

        public Csv(string filename, char separator = ';') :
            this(filename, separator, Utils.UTF8) {
        }

        public Csv(string filename, char separator, Encoding encoding) {
            _writer = new StreamWriter(filename, false, encoding);
            Separator = separator;
        }

        public void Dispose() {
            GC.SuppressFinalize(this);
            _writer.Dispose();
        }

        public ValueTask DisposeAsync() {
            GC.SuppressFinalize(this);
            return _writer.DisposeAsync();
        }

        public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) {
            progress?.Report(0.0);
            int count = data.Count() + 2, i = 0;

            if (Header != null) await _writer.WriteLineAsync(Header);
            progress?.Report(100.0 * ++i / count);

            foreach (var row in data) {
                await _writer.WriteLineAsync(FormatRow(row));
                progress?.Report(100.0 * ++i / count);
            }

            await _writer.FlushAsync();
            progress?.Report(100.0);
        }

        public void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
            ExportAsync(data, progress).GetAwaiter().GetResult();
        }

        public string FormatRow(IEnumerable row) {
            return string.Join(Separator, row);
        }

        public abstract string FormatRow(T row);
    }

    public class CsvSimple : Csv<IEnumerable<object?>> {

        public CsvSimple(string filename, char separator, Encoding encoding) :
            base(filename, separator, encoding) {
        }

        public CsvSimple(string filename, char separator = ';') :
            base(filename, separator) {
        }

        public override string FormatRow(IEnumerable<object?> row) {
            return string.Join(Separator, row.Select(i => {
                var str = $"{i}";
                return str.Contains(Separator) || str.Contains('\n') ? $"\"{str.Replace("\"", "\"\"")}\"" : str;
            }));
        }
    }
}
