using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using PolyTraderSharp.Models;

namespace PolyTrader.Modules.CopyTrading.ConfigImport
{
    /// <summary>
    /// Parst mongoexport-JSON (Standard-Array, KEIN Extended JSON) in die Zielmodelle.
    /// Robust gegen die typischen Export-Eigenheiten: Dezimalwerte als Strings ("0.5"),
    /// <c>null</c>-Felder, fehlende Felder (→ Default). Bewusst als reine, testbare Logik
    /// getrennt von der DB-Anbindung im ConfigMigrator.
    /// </summary>
    public static class MongoExportParser
    {
        /// <summary>Ein Account-Dokument liefert die Core-Account-Daten UND die
        /// (im Alt-Dokument mitgeführten) Copytrading-Detail-Einstellungen.</summary>
        public sealed record AccountImport(AccountState Account, CopyTradingAccountSettings Settings);

        public static List<AccountImport> ParseAccounts(string json)
        {
            var result = new List<AccountImport>();
            using var doc = JsonDocument.Parse(json);
            if (doc.RootElement.ValueKind != JsonValueKind.Array) return result;

            foreach (var el in doc.RootElement.EnumerateArray())
            {
                var acc = new AccountState
                {
                    AccountId = el.GetProperty("_id").GetInt32(),
                    Name = Str(el, "Name"),
                    WalletAddress = Str(el, "WalletAddress"),
                    ApiKey = Str(el, "ApiKey"),
                    ApiSecret = Str(el, "ApiSecret"),
                    ApiPassphrase = Str(el, "ApiPassphrase"),
                    PrivateKey = Str(el, "PrivateKey"),
                    IsDemo = Bool(el, "IsDemo"),
                    IsActive = Bool(el, "IsActive", true),
                    CloseOnlyMode = Bool(el, "CloseOnlyMode"),
                    PayoutAddress = Str(el, "PayoutAddress"),
                    PayoutLimitUsd = Dec(el, "PayoutLimitUsd", 0m),
                    TotalBalance = Dec(el, "TotalBalance", 0m),
                    AvailableBalance = Dec(el, "AvailableBalance", 0m),
                    HasOpenLimitOrders = Bool(el, "HasOpenLimitOrders")
                };

                var settings = new CopyTradingAccountSettings
                {
                    AccountId = acc.AccountId,
                    PerMarketLimit = Dec(el, "PerMarketLimit", 5.0m),
                    MaxPriceDifference = Dec(el, "MaxPriceDifference", 2.0m),
                    MaxBuyPrice = Dec(el, "MaxBuyPrice", 0.98m),
                    // Fehlt ProfitTarget im Export: dormant (9999), NICHT 50 – sonst würde ein erneuter
                    // --migrate-json-Lauf Take-Profit bei +50% unbeabsichtigt scharf schalten (verdrahtet).
                    ProfitTarget = Dec(el, "ProfitTarget", 9999.0m),
                    PreRedeemLimit = Dec(el, "PreRedeemLimit", 0.0m),
                    PerMasterLimit = Dec(el, "PerMasterLimit", 10.0m),
                    perMaxTime6h = Dec(el, "perMaxTime6h", 20.0m),
                    perMaxTime24h = Dec(el, "perMaxTime24h", 20.0m),
                    perMaxTime72h = Dec(el, "perMaxTime72h", 20.0m),
                    perMaxTimeNone = Dec(el, "perMaxTimeNone", 40.0m)
                };

                result.Add(new AccountImport(acc, settings));
            }
            return result;
        }

        public static List<TrackedTrader> ParseTraders(string json)
        {
            var result = new List<TrackedTrader>();
            using var doc = JsonDocument.Parse(json);
            if (doc.RootElement.ValueKind != JsonValueKind.Array) return result;

            foreach (var el in doc.RootElement.EnumerateArray())
            {
                result.Add(new TrackedTrader
                {
                    Id = el.GetProperty("_id").GetInt32(),
                    WalletAddress = Str(el, "WalletAddress"),
                    DisplayName = Str(el, "DisplayName"),
                    Category = Str(el, "Category", "NEW_BIG_BET"),
                    Description = Str(el, "Description"),
                    Reasoning = Str(el, "Reasoning"),
                    IsActive = Bool(el, "IsActive", true),
                    IsHidden = Bool(el, "IsHidden"),
                    TotalTrades = Int(el, "TotalTrades"),
                    WinningTrades = Int(el, "WinningTrades"),
                    Winrate30t = Dbl(el, "Winrate30t"),
                    TotalPnl = Dbl(el, "TotalPnl"),
                    AssignedAccountIds = IntSet(el, "AssignedAccountIds")
                });
            }
            return result;
        }

        // --- JSON-Helfer (mongoexport: Dezimalwerte als Strings, null möglich) ---
        private static string Str(JsonElement el, string name, string fallback = "")
        {
            if (el.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String)
                return v.GetString() ?? fallback;
            return fallback;
        }

        private static bool Bool(JsonElement el, string name, bool fallback = false)
        {
            if (el.TryGetProperty(name, out var v))
            {
                if (v.ValueKind == JsonValueKind.True) return true;
                if (v.ValueKind == JsonValueKind.False) return false;
            }
            return fallback;
        }

        private static int Int(JsonElement el, string name, int fallback = 0)
        {
            if (el.TryGetProperty(name, out var v))
            {
                if (v.ValueKind == JsonValueKind.Number && v.TryGetInt32(out var n)) return n;
                if (v.ValueKind == JsonValueKind.String && int.TryParse(v.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var s)) return s;
            }
            return fallback;
        }

        private static double Dbl(JsonElement el, string name, double fallback = 0)
        {
            if (el.TryGetProperty(name, out var v))
            {
                if (v.ValueKind == JsonValueKind.Number && v.TryGetDouble(out var n)) return n;
                if (v.ValueKind == JsonValueKind.String && double.TryParse(v.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var s)) return s;
            }
            return fallback;
        }

        private static decimal Dec(JsonElement el, string name, decimal fallback)
        {
            if (el.TryGetProperty(name, out var v))
            {
                if (v.ValueKind == JsonValueKind.Number && v.TryGetDecimal(out var n)) return n;
                if (v.ValueKind == JsonValueKind.String && decimal.TryParse(v.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var s)) return s;
            }
            return fallback;
        }

        private static HashSet<int> IntSet(JsonElement el, string name)
        {
            var set = new HashSet<int>();
            if (el.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.Array)
            {
                foreach (var item in v.EnumerateArray())
                {
                    if (item.ValueKind == JsonValueKind.Number && item.TryGetInt32(out var n)) set.Add(n);
                    else if (item.ValueKind == JsonValueKind.String && int.TryParse(item.GetString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var s)) set.Add(s);
                }
            }
            return set;
        }
    }
}
