using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace WikipediaRagWinForms.Services
{
    public class WikidataFacts
    {
        public string LabelDe { get; set; }
        public string LabelEn { get; set; }
        public string LabelEs { get; set; }

        public string DescriptionDe { get; set; }
        public string DescriptionEn { get; set; }
        public string DescriptionEs { get; set; }

        public List<string> Positions { get; set; } = new List<string>();
        public string StartTime { get; set; }
        public string EndTime { get; set; }
        public string Replaces { get; set; }
        public string ReplacedBy { get; set; }
        public string BirthDate { get; set; }
    }

    public class WikidataClient
    {
        private readonly HttpClient _http;

        public WikidataClient()
        {
            _http = new HttpClient
            {
                Timeout = TimeSpan.FromMilliseconds(3000)  // HARTER Timeout
            };

            _http.DefaultRequestHeaders.Add(
                "User-Agent",
                "LocalAI-Wikipedia-RAG/1.0"
            );
        }

        // --- Sichere GET-Anfrage ---
        private async Task<string> SafeGetAsync(string url, CancellationToken token)
        {
            CancellationTokenSource cts = null;
            try
            {
                cts = CancellationTokenSource.CreateLinkedTokenSource(token);
                cts.CancelAfter(3000);

                HttpResponseMessage resp = await _http.GetAsync(url, cts.Token).ConfigureAwait(false);
                if (!resp.IsSuccessStatusCode)
                    return "";

                return await resp.Content.ReadAsStringAsync().ConfigureAwait(false);
            }
            catch
            {
                return "";
            }
            finally
            {
                if (cts != null)
                    cts.Dispose();
            }
        }


        // --- QID suchen ---
        public async Task<string> SearchQidAsync(string term, CancellationToken token)
        {
            if (string.IsNullOrWhiteSpace(term))
                return "";

            term = term.Trim();

            // 1) ERSTER VERSUCH: kompletter Begriff (z.B. "Angela Merkel")
            string qid = await SearchQidSingleAsync(term, token);
            if (!string.IsNullOrEmpty(qid))
                return qid;

            // 2) FALLBACK: für Rollen wie "Bundeskanzler Deutschland" → erstes Wort
            string lower = term.ToLowerInvariant();
            if (lower.Contains("bundeskanzler") ||
                lower.Contains("kanzler") ||
                lower.Contains("präsident") ||
                lower.Contains("minister"))
            {
                string firstWord = term.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[0];
                if (!string.IsNullOrWhiteSpace(firstWord) &&
                    !firstWord.Equals(term, StringComparison.OrdinalIgnoreCase))
                {
                    qid = await SearchQidSingleAsync(firstWord, token);
                    if (!string.IsNullOrEmpty(qid))
                        return qid;
                }
            }

            // nichts gefunden
            return "";
        }

        // Hilfsfunktion: genau ein Search-Call
        private async Task<string> SearchQidSingleAsync(string searchTerm, CancellationToken token)
        {
            string url =
                "https://www.wikidata.org/w/api.php?action=wbsearchentities" +
                "&format=json&language=de&search=" +
                Uri.EscapeDataString(searchTerm);

            string json = await SafeGetAsync(url, token);

            if (string.IsNullOrWhiteSpace(json))
                return "";

            try
            {
                JObject obj = JObject.Parse(json);
                var first = obj["search"]?.First;
                if (first == null)
                    return "";

                return (string)first["id"] ?? "";
            }
            catch
            {
                return "";
            }
        }


        // --- Entity laden ---
        private async Task<JObject> LoadEntityAsync(string qid, CancellationToken token)
        {
            string url =
                "https://www.wikidata.org/w/api.php?action=wbgetentities" +
                "&format=json&ids=" + Uri.EscapeDataString(qid);

            string json = await SafeGetAsync(url, token);

            if (string.IsNullOrWhiteSpace(json))
                return null;

            try
            {
                return JObject.Parse(json);
            }
            catch
            {
                return null;
            }
        }

        // --- Hauptfunktion ---
        public async Task<WikidataFacts> GetFactsAsync(string term, CancellationToken token)
        {
            var facts = new WikidataFacts();

            // 1) Q-ID suchen
            string qid = await SearchQidAsync(term, token);
            if (string.IsNullOrWhiteSpace(qid))
                return facts;

            // 2) Entity laden
            JObject entRoot = await LoadEntityAsync(qid, token);
            if (entRoot == null)
                return facts;

            var entity = entRoot["entities"]?[qid];
            if (entity == null)
                return facts;

            // 3) Label / Description
            try
            {
                facts.LabelDe = (string)entity["labels"]?["de"]?["value"] ?? "";
                facts.LabelEn = (string)entity["labels"]?["en"]?["value"] ?? "";
                facts.LabelEs = (string)entity["labels"]?["es"]?["value"] ?? "";

                facts.DescriptionDe = (string)entity["descriptions"]?["de"]?["value"] ?? "";
                facts.DescriptionEn = (string)entity["descriptions"]?["en"]?["value"] ?? "";
                facts.DescriptionEs = (string)entity["descriptions"]?["es"]?["value"] ?? "";
            }
            catch
            {
                // ignorieren – bleibt leer
            }

            System.Diagnostics.Debug.WriteLine(
                $"WIKIDATA LABELS: DE='{facts.LabelDe}', EN='{facts.LabelEn}', ES='{facts.LabelEs}'");

            System.Diagnostics.Debug.WriteLine(
                $"WIKIDATA DESCR: DE='{facts.DescriptionDe}', EN='{facts.DescriptionEn}', ES='{facts.DescriptionEs}'");


            // 4) Claims (Properties)
            var claims = entity["claims"] as JObject;
            if (claims == null)
                return facts;

            try
            {
                // P39 – Position held (für Personen)
                var p39 = claims["P39"];
                if (p39 != null)
                {
                    foreach (var c in p39)
                    {
                        string posId = (string)c["mainsnak"]?["datavalue"]?["value"]?["id"];
                        if (!string.IsNullOrWhiteSpace(posId))
                        {
                            string posLabel = await ResolveLabelAsync(posId, token);
                            if (!string.IsNullOrWhiteSpace(posLabel) &&
                                !facts.Positions.Contains(posLabel))
                            {
                                facts.Positions.Add(posLabel);
                            }
                        }

                        // Qualifier: P580 / P582 (Start/Ende)
                        var qualifiers = c["qualifiers"];
                        if (qualifiers != null)
                        {
                            var qStart = qualifiers["P580"];
                            if (qStart != null && qStart.HasValues)
                            {
                                string t = (string)qStart[0]["datavalue"]?["value"]?["time"];
                                if (!string.IsNullOrWhiteSpace(t))
                                    facts.StartTime = NormalizeTimeString(t);
                            }

                            var qEnd = qualifiers["P582"];
                            if (qEnd != null && qEnd.HasValues)
                            {
                                string t = (string)qEnd[0]["datavalue"]?["value"]?["time"];
                                if (!string.IsNullOrWhiteSpace(t))
                                    facts.EndTime = NormalizeTimeString(t);
                            }
                        }
                    }
                }

                // Vorgänger – P1365
                var p1365 = claims["P1365"];
                if (p1365 != null && p1365.HasValues)
                {
                    string prevId =
                        (string)p1365[0]["mainsnak"]?["datavalue"]?["value"]?["id"];
                    if (!string.IsNullOrWhiteSpace(prevId))
                        facts.Replaces = await ResolveLabelAsync(prevId, token);
                }

                // Nachfolger – P1366
                var p1366 = claims["P1366"];
                if (p1366 != null && p1366.HasValues)
                {
                    string nextId =
                        (string)p1366[0]["mainsnak"]?["datavalue"]?["value"]?["id"];
                    if (!string.IsNullOrWhiteSpace(nextId))
                        facts.ReplacedBy = await ResolveLabelAsync(nextId, token);
                }

                // Geburtsdatum – P569
                var p569 = claims["P569"];
                if (p569 != null && p569.HasValues)
                {
                    string t =
                        (string)p569[0]["mainsnak"]?["datavalue"]?["value"]?["time"];
                    if (!string.IsNullOrWhiteSpace(t))
                        facts.BirthDate = NormalizeTimeString(t);
                }
            }
            catch
            {
                // Fehler bei Einzel-Properties ignorieren
            }

            // --- Positions-Bereinigung: Dubletten raus + sortieren ---
            if (facts.Positions != null && facts.Positions.Count > 0)
            {
                var cleaned = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
                foreach (var p in facts.Positions)
                {
                    if (!string.IsNullOrWhiteSpace(p))
                        cleaned.Add(p.Trim());
                }

                facts.Positions = cleaned
                    .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
                    .ToList();
            }

            return facts;

        }


        private static string NormalizeTimeString(string raw)
        {
            if (string.IsNullOrEmpty(raw))
                return "";

            // Führendes '+' entfernen
            raw = raw.Trim().Trim('+');

            // Nur Datumsteil behalten, falls vorhanden
            if (raw.Length >= 10)
                return raw.Substring(0, 10);

            return raw;
        }

        public static string FormatGermanDate(string isoDate)
        {
            if (string.IsNullOrWhiteSpace(isoDate))
                return "";

            // Erwartet: YYYY-MM-DD
            if (!DateTime.TryParse(isoDate, out DateTime dt))
                return isoDate;

            return dt.ToString("dd. MMMM yyyy",
                System.Globalization.CultureInfo.GetCultureInfo("de-DE"));
        }


        private async Task<string> ResolveLabelAsync(string qid, CancellationToken token)
        {
            if (string.IsNullOrWhiteSpace(qid))
                return "";

            string url =
                "https://www.wikidata.org/w/api.php?action=wbgetentities" +
                "&format=json&props=labels&languages=de|en|es&ids=" + Uri.EscapeDataString(qid);

            string json = await SafeGetAsync(url, token);
            if (string.IsNullOrWhiteSpace(json))
                return "";

            try
            {
                JObject root = JObject.Parse(json);
                var ent = root["entities"]?[qid];
                if (ent == null)
                    return "";

                string label =
                (string)ent["labels"]?["de"]?["value"] ??
                (string)ent["labels"]?["en"]?["value"] ??
                (string)ent["labels"]?["es"]?["value"];

                return label ?? "";
            }
            catch
            {
                return "";
            }
        }


    }
}
