using System.Globalization;
using CsvHelper;
using CsvHelper.Configuration;
using ExperionCrawler.Core.Application.Interfaces;
using ExperionCrawler.Core.Domain.Entities;
using Microsoft.Extensions.Logging;

namespace ExperionCrawler.Infrastructure.Csv;

public class ExperionCsvService : IExperionCsvService
{
    private const string CsvDir = "data/csv";
    private readonly ILogger<ExperionCsvService> _logger;

    public ExperionCsvService(ILogger<ExperionCsvService> logger)
    {
        _logger = logger;
        Directory.CreateDirectory(CsvDir);
    }

    public async Task<string> ExportAsync(IEnumerable<ExperionRecord> records, string fileName)
    {
        var path   = Path.Combine(CsvDir, fileName.EndsWith(".csv") ? fileName : $"{fileName}.csv");
        var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true };

        await using var writer = new StreamWriter(path);
        await using var csv    = new CsvWriter(writer, config);
        await csv.WriteRecordsAsync(records);

        _logger.LogInformation("[ExperionCsv] 내보내기 완료: {Path} ({Count}건)", path, records.Count());
        return path;
    }

    public async Task<IEnumerable<ExperionRecord>> ImportAsync(string filePath)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException($"CSV 파일을 찾을 수 없습니다: {filePath}");

        // I/O 작업이므로 Task.Run으로 백그라운드 스레드에서 실행
        var records = await Task.Run(() =>
        {
            using var reader = new StreamReader(filePath);
            using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
            return csv.GetRecords<ExperionRecord>().ToList();
        });

        _logger.LogInformation("[ExperionCsv] 가져오기 완료: {Path} ({Count}건)", filePath, records.Count);
        return records;
    }

    public IEnumerable<string> GetAvailableFiles()
    {
        if (!Directory.Exists(CsvDir)) return Enumerable.Empty<string>();
        return Directory.GetFiles(CsvDir, "*.csv")
            .Select(Path.GetFileName)
            .Where(f => f != null)
            .Cast<string>()
            .OrderByDescending(f => f);
    }

    /// <summary>
    /// 노드맵 엔트리를 AssetLoader가 읽을 수 있는 형식으로 CSV 저장
    /// 헤더: level,class,name,node_id,data_type
    /// </summary>
    public async Task<string> ExportNodeMapAsync(IEnumerable<ExperionNodeMapEntry> entries, string fileName)
    {
        var path = Path.Combine(CsvDir, fileName.EndsWith(".csv") ? fileName : $"{fileName}.csv");

        await using var writer = new StreamWriter(path, append: false, encoding: System.Text.Encoding.UTF8);
        await writer.WriteLineAsync("level,class,name,node_id,data_type");

        int count = 0;
        foreach (var e in entries)
        {
            await writer.WriteLineAsync(
                $"{e.Level},{EscapeCsv(e.NodeClass)},{EscapeCsv(e.DisplayName)},{EscapeCsv(e.NodeId)},{EscapeCsv(e.DataType)}");
            count++;
        }

        _logger.LogInformation("[ExperionCsv] 노드맵 내보내기 완료: {Path} ({Count}건)", path, count);
        return path;
    }

    private static string EscapeCsv(string value)
    {
        if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
            return $"\"{value.Replace("\"", "\"\"")}\"";
        return value;
    }
}
