using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using System.Linq;
using UnityEngine.UI;

public class FactExplorer : MonoBehaviour
{
    [Header("PrefabComponents")]
    [SerializeField] private Transform factParentsUI;
    [SerializeField] private Transform mainFactUI;
    [SerializeField] private Transform factChildrenUI;
    [SerializeField] private Transform linesUI;

    [Header("Prefabs")]
    [SerializeField] private GameObject factSpotPrefab;


    private Fact mainFact;
    private List<Fact> parentFacts;
    private List<Fact> childFacts;


    private void Update()
    {
        DestroyIfClickedOutside();
    }

    public void Initialize(Fact fact)
    {

        mainFact = fact;
        parentFacts = GetParentFacts();
        childFacts = GetChildFacts();

        Debug.Log($"Parents of {mainFact.Label}:  {string.Join(", ", parentFacts.Select(cf => cf.Label))}");
        Debug.Log($"Children of {mainFact.Label}: {string.Join(", ", childFacts.Select(cf => cf.Label))}");

        UpdateFactExplorerUI();
    }

    #region Implementation
    private List<Fact> GetParentFacts()
    {   
        //TODO: TSC how to get all current facts without this dumb workaround
        var allFacts = DisplayFacts.displayedFacts.Values.Select(x => x.GetComponent<FactWrapper>().fact);
        List<Fact> _parentFacts = new();

        // get all facts that reference the mainFact
        foreach (Fact f in allFacts)
            if (GetReferencedPids(f).Contains(mainFact.Id)) // if f contains reference to mainFact
                _parentFacts.Add(f);

        return _parentFacts;
    }

    private List<Fact> GetChildFacts()
    {
        List<Fact> childFacts = new();

        // get all properties of Type mainFactType that are strings and start with "pid"
        foreach (string factId in GetReferencedPids(mainFact))
            childFacts.Add(StageStatic.stage.factState[factId]);

        return childFacts;
    }

    private void UpdateFactExplorerUI()
    {
        SpawnUIFacts(factParentsUI, parentFacts);
        SpawnUIFacts(mainFactUI, new List<Fact>() { mainFact });
        SpawnUIFacts(factChildrenUI, childFacts);
    }

    private void DestroyIfClickedOutside()
    {
        if ((Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) &&
            !RectTransformUtility.RectangleContainsScreenPoint(transform.GetComponent<RectTransform>(), Input.mousePosition, null))
        {
            Destroy(gameObject);
        }
    }
    #endregion Implementation

    #region Helper
    /// <summary>
    /// Check all fields of a Fact for references to other facts and return their ids
    /// </summary>
    /// <param name="f"></param>
    /// <returns>An Enumerable containing the ids of facts referenced by the input fact</returns>
    private static IEnumerable<string> GetReferencedPids(Fact f)
    {
        return (f.GetType().GetFields()
            .Where(field => field.FieldType == typeof(string) && field.Name.ToLower().StartsWith("pid"))
        ).Select(fi => (string)fi.GetValue(f));
    }

    private void SpawnUIFacts(Transform uiParent, List<Fact> toSpawn)
    {
        uiParent.gameObject.DestroyAllChildren();
        foreach (Fact f in toSpawn)
        {
            var spot = Instantiate(factSpotPrefab, uiParent);

            // TODO: this link to DisplayFacts is not ideal: maybe refactor to SciptableObject or such
            var display = f.instantiateDisplay(DisplayFacts.prefabDictionary[f.GetType()], spot.transform);
            display.transform.localPosition = Vector3.zero;
        }
    }
    #endregion Helper
}