﻿using System;
using System.Collections.Generic;
using HutongGames.PlayMaker;
using UnityEngine;

namespace BansheeGz.BGDatabase
{
    [ActionCategory("BansheeGz")]
    [HutongGames.PlayMaker.Tooltip("Reset fields values to default values for provided meta (table)")]
    public class BGPlaymakerResetValuesTable : BGPlaymakerResetValuesTableBasedA
    {
        [HutongGames.PlayMaker.Tooltip("If this parameter is set to true and the matching rows values are reverted to default values")]
        public FsmBool updateMatchingRows;

        [HutongGames.PlayMaker.Tooltip("[Optional] if the value is set, the matching rows with provided indexes will be affected only")] [UIHint(UIHint.Variable)] [ArrayEditor(VariableType.Int)]
        public FsmArray includedRowsIndexesForUpdate;


        [HutongGames.PlayMaker.Tooltip("If this parameter is set to true and the row is not found in the loaded repo, but exists in default repo, the row will be added")]
        public FsmBool addMissingRows;

        [HutongGames.PlayMaker.Tooltip("If this parameter is set to true and the row is not found in the default repo, the row will be removed")]
        public FsmBool removeOrphanedRows;


        public override void Reset()
        {
            base.Reset();
            updateMatchingRows = new FsmBool() { Value = true };
            addMissingRows = null;
            removeOrphanedRows = null;
        }

        public override void OnEnter()
        {
            try
            {
                var meta = Meta;
                meta.Repo.Events.Batch(() =>
                {
                    var fields = GetFields(meta);
                    var updateMatching = IsOn(updateMatchingRows);
                    var addMissing = IsOn(addMissingRows);
                    var removeOrphaned = IsOn(removeOrphanedRows);
                    if (!updateMatching && !addMissing && !removeOrphaned)
                    {
                        DebugMessage("No actions chosen: updateMatching, addMissing, removeOrphaned are all set to false! Skipping...");
                        return;
                    }

                    var defaultMeta = GetDefaultMeta(meta);
                    var defaultFields = GetDefaultFields(defaultMeta, fields);

                    var orphaned = removeOrphaned ? new List<BGEntity>() : null;
                    var updated = 0;
                    var removed = 0;
                    var added = 0;

                    Predicate<BGEntity> filter = null;
                    if (!includedRowsIndexesForUpdate.IsNone)
                    {
                        var values = includedRowsIndexesForUpdate.Values;
                        var indexes = new HashSet<int>();
                        if (values != null)
                        {
                            foreach (var value in values)
                            {
                                var index = (int)value;
                                if (index < 0 || index >= meta.CountEntities)
                                    throw new Exception($"Table {meta.Name}: can not retrieve entity by index, " +
                                                        $"index={index} is out of bounds(0, {meta.CountEntities - 1})!");
                                indexes.Add(index);
                            }
                        }
                        filter = e => indexes.Contains(e.Index);
                    }

                    meta.ForEachEntity(entity =>
                    {
                        var defaultEntity = GetDefaultEntity(defaultMeta, entity, false);
                        if (defaultEntity != null)
                        {
                            if (updateMatching && (filter == null || filter.Invoke(entity)))
                            {
                                updated++;
                                Copy(defaultEntity, defaultFields, entity, fields);
                            }
                        }
                        else orphaned?.Add(entity);
                    });

                    if (orphaned?.Count > 0)
                    {
                        removed = orphaned.Count;
                        meta.DeleteEntities(orphaned);
                    }

                    if (addMissing) added = AddMissing(defaultMeta, defaultFields, meta, fields);

                    DebugMessage($"Table={meta.Name}, fields count={fields.Count}, {updated} rows reverted, {removed} rows removed, {added} rows added");
                    meta.Repo.Events.EnsureBatch().AddMetaWithUpdatedEntities(meta.Id);
                });
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                throw;
            }

            Finish();
        }
    }
}