using AccountTracking.Api.Data;
using AccountTracking.Api.Models;
using AccountTracking.Api.Models.Dtos;

namespace AccountTracking.Api.Services;

public class CsvImportService(AppDbContext db)
{
    private static readonly string[] RequiredColumns = ["částka", "ID transakce", "datum zaúčtování"];

    public async Task<ImportServiceResult> ImportAsync(string csvContent, string filename)
    {
        var lines = csvContent
            .Split('\n', StringSplitOptions.None)
            .Select(l => l.TrimEnd('\r'))
            .ToArray();

        // Find header row by scanning for line starting with "číslo účtu"
        int headerIndex = -1;
        for (int i = 0; i < lines.Length; i++)
        {
            if (lines[i].StartsWith("číslo účtu", StringComparison.OrdinalIgnoreCase))
            {
                headerIndex = i;
                break;
            }
        }

        if (headerIndex < 0)
            return ImportServiceResult.Failure("Soubor neobsahuje správnou hlavičku (řádek začínající 'číslo účtu').");

        var headers = lines[headerIndex].Split(';');
        var columnIndex = BuildColumnIndex(headers);

        // Validate required columns
        foreach (var col in RequiredColumns)
        {
            if (!columnIndex.ContainsKey(col))
                return ImportServiceResult.Failure($"Souboru chybí povinný sloupec: '{col}'.");
        }

        // Load existing transaction IDs to detect duplicates
        var existingIds = db.Transactions
            .Select(t => t.TransactionId)
            .ToHashSet();

        int imported = 0, skipped = 0;
        var toInsert = new List<Transaction>();

        for (int i = headerIndex + 1; i < lines.Length; i++)
        {
            var line = lines[i];
            if (string.IsNullOrWhiteSpace(line)) continue;

            var fields = line.Split(';');
            if (fields.Length < headers.Length) continue;

            var txId = GetField(fields, columnIndex, "ID transakce");
            if (string.IsNullOrEmpty(txId)) continue;

            if (existingIds.Contains(txId))
            {
                skipped++;
                continue;
            }

            existingIds.Add(txId); // prevent duplicates within the same file

            toInsert.Add(new Transaction
            {
                AccountNumber = GetField(fields, columnIndex, "číslo účtu"),
                BookingDate = DateOnly.ParseExact(GetField(fields, columnIndex, "datum zaúčtování"), "d.M.yyyy"),
                Amount = ParseCzechDecimal(GetField(fields, columnIndex, "částka")),
                Currency = GetField(fields, columnIndex, "měna"),
                Balance = ParseCzechDecimal(GetField(fields, columnIndex, "zůstatek")),
                CounterPartyName = GetField(fields, columnIndex, "jméno protistrany"),
                OperationDescription = GetField(fields, columnIndex, "označení operace"),
                Message = GetField(fields, columnIndex, "zpráva"),
                Category = GetField(fields, columnIndex, "kategorie"),
                VariableSymbol = GetField(fields, columnIndex, "variabilní symbol"),
                BankNote = GetField(fields, columnIndex, "vlastní poznámka"),
                TransactionId = txId,
                CounterBankCode = GetField(fields, columnIndex, "kód banky protiúčtu"),
                ConstantSymbol = GetField(fields, columnIndex, "konstantní symbol"),
                SpecificSymbol = GetField(fields, columnIndex, "specifický symbol"),
                OrderName = GetField(fields, columnIndex, "název trvalého příkazu"),
                ExchangeRate = GetField(fields, columnIndex, "kurz"),
                E2EId = GetField(fields, columnIndex, "E2E identifikace"),
                PayerReference = GetField(fields, columnIndex, "reference plátce"),
                OriginalPayer = GetField(fields, columnIndex, "původní plátce"),
                FinalRecipient = GetField(fields, columnIndex, "konečný příjemce"),
                OriginalTransaction = GetField(fields, columnIndex, "původní transakce"),
            });
            imported++;
        }

        if (toInsert.Count > 0)
        {
            db.Transactions.AddRange(toInsert);
            db.ImportLogs.Add(new ImportLog
            {
                Filename = filename,
                RecordsImported = imported,
                RecordsSkipped = skipped,
                ImportedAt = DateTime.UtcNow
            });
            await db.SaveChangesAsync();
        }

        return ImportServiceResult.Success(new ImportResult(imported, skipped));
    }

    public static decimal ParseCzechDecimal(string value)
    {
        // Czech format: "1 740,80" → remove spaces, replace comma with dot
        var normalised = value.Replace(" ", "").Replace(",", ".");
        return decimal.Parse(normalised, System.Globalization.CultureInfo.InvariantCulture);
    }

    private static Dictionary<string, int> BuildColumnIndex(string[] headers)
        => headers
            .Select((h, i) => (Header: h.Trim(), Index: i))
            .Where(x => !string.IsNullOrEmpty(x.Header))
            .ToDictionary(x => x.Header, x => x.Index, StringComparer.OrdinalIgnoreCase);

    private static string GetField(string[] fields, Dictionary<string, int> index, string column)
    {
        if (!index.TryGetValue(column, out int i)) return "";
        return i < fields.Length ? fields[i].Trim() : "";
    }
}

public class ImportServiceResult
{
    public bool IsSuccess { get; private init; }
    public ImportResult? Value { get; private init; }
    public string? Error { get; private init; }

    public static ImportServiceResult Success(ImportResult value)
        => new() { IsSuccess = true, Value = value };

    public static ImportServiceResult Failure(string error)
        => new() { IsSuccess = false, Error = error };
}
