using FamilyTreeAPI.Entities;
using FamilyTreeAPI.Models;
using Microsoft.EntityFrameworkCore;
using System.Drawing.Text;
namespace FamilyTreeAPI.Repository;

public partial class PersonRepository
{
    private TreeNode<string> PopulateItem(Person model)
    {
        TreeNode<string> treeNode = new();
        treeNode.Label = model.FirstName;
        treeNode.Data = model.Id.ToString();
        treeNode.Key = model.Id.ToString();
        treeNode.Expanded = false;
        treeNode.Children = new();
        return treeNode;
    }
    private bool UseFamilyCriteria(FamilyCriteria criteria, Person person)
    {
        bool result = false;
        if (criteria.UseFather && criteria.UseMother)
        {
            result = person.FatherId.GetValueOrDefault(0) == 0 
                && person.MotherId.GetValueOrDefault(0) == 0; 
        }
        else if (criteria.UseFather)
        {
            result = person.FatherId.GetValueOrDefault(0) == 0;
        }
        else if (criteria.UseMother)
        {
            result = person.MotherId.GetValueOrDefault(0) == 0;
        }

        return result;
    }
    private bool UseChildFamilyCriteria(FamilyCriteria criteria, int id, Person person)
    {
        bool result = false;
        int parentId;
        if (criteria.UseFather)
        {
            parentId = person.FatherId.GetValueOrDefault(0);
            result = parentId == id;
        }
        else if (criteria.UseMother)
        {
            parentId = person.MotherId.GetValueOrDefault(0);
            result = parentId == id;
        }

        return result;
    }
    private List<Person> SplitListOfTopLevel(List<Person> list, FamilyCriteria criteria)
    {
        List<Person> result = new();
        Person item;
        for (int i = list.Count - 1; i > -1; i--)
        {
            item = list[i];
            if (UseFamilyCriteria(criteria,item))
            {
                result.Add(item);
                list.RemoveAt(i);
            }

        }
        return result;
    }

    private List<Person> GetParentId(List<Person> list,int id, FamilyCriteria criteria, Func<FamilyCriteria, int, Person, bool> conditionFn)
    {
        List<Person> result = new();
        Person item;
        for (int i = list.Count - 1; i > -1; i--)
        {
            item = list[i];
            if (conditionFn(criteria,id, item))
            {
                result.Add(item);
                list.RemoveAt(i);
            }

        }
        return result;
    }

    private List<TreeNode<string>> PopulateChild(FamilyCriteria criteria,int id, List<Person> childList, Func<FamilyCriteria, int ,Person,bool> conditionFn)
    {
        Person person;
        TreeNode<string> treeNode;
        List<TreeNode<string>> list = new();
        List<Person> children = GetParentId(childList,id, criteria, conditionFn);
        for (int i = 0; i < children.Count; i++)
        {
            person = children[i];
            treeNode = PopulateItem(person);
            list.Add(treeNode);
            // get children of this one too
            treeNode.Children = PopulateChild(criteria, person.Id, childList, UseChildFamilyCriteria);
        }
        return list;
    }
    public async Task<ResultModel<List<TreeNode<string>>> > GetFamilyTreeBy(FamilyCriteria criteria)
    {
        int statusCode = -1;
        string error = "";
        Person person;
        TreeNode<string> treeNode;
        List<TreeNode<string>> data = new();
        try
        {
            var personList = await _context.Persons.ToListAsync();
            var topList = SplitListOfTopLevel(personList, criteria);
            for (int i = 0; i < topList.Count; i++)
            {
                person = topList[i];
                treeNode = PopulateItem(person);
                data.Add(treeNode);
                treeNode.Children = PopulateChild(criteria, person.Id, personList, UseChildFamilyCriteria);

            }
            statusCode = 1;
        }
        catch (Exception ex)
        {
            error = ex.ToString();
            statusCode = -1;
        }

        return new ResultModel<List<TreeNode<string>>>
            { 
            Data = data,
            StatusCode = statusCode,
            Message = error 
            };
        
    }
}
