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

namespace BansheeGz.BGDatabase
{
    [ActionCategory("BansheeGz")]
    [HutongGames.PlayMaker.Tooltip("Reset values to default values for provided metas(tables)")]
    public class BGPlaymakerResetValuesTableList : BGPlaymakerResetValuesA
    {
        [HutongGames.PlayMaker.Tooltip("Comma separated list of meta names, if omitted, all metas will be processed")]
        public FsmString metas;

        [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("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();
            metas = null;
            updateMatchingRows = new FsmBool() { Value = true };
            addMissingRows = null;
            removeOrphanedRows = null;
        }

        public override void OnEnter()
        {
            try
            {
                BGRepo.I.Events.Batch(() =>
                {
                    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 metas = GetTables(BGRepo.I);
                    var defaultRepo = DefaultRepo;
                    var metaCounts = 0;
                    var updated = 0;
                    var removed = 0;
                    var added = 0;
                    foreach (var meta in metas)
                    {
                        var defaultMeta = defaultRepo.GetMeta(meta.Id);
                        if (defaultMeta == null)
                        {
                            Debug.Log($"Can not find a meta with id={meta.Id} (name={meta.Name}) in default database!");
                            continue;
                        }

                        metaCounts++;
                        
                        var orphaned = removeOrphaned ? new List<BGEntity>() : null;

                        var fields = new List<BGField>();
                        meta.FindFields(fields);
                        var defaultFields = GetDefaultFields(defaultMeta, fields);
                        meta.ForEachEntity(entity =>
                        {
                            var defaultEntity = defaultMeta.GetEntity(entity.Id);
                            if (defaultEntity == null) orphaned?.Add(entity);
                            else
                            {
                                if (updateMatching)
                                {
                                    updated++;
                                    Copy(defaultEntity, defaultFields, entity, fields);
                                }
                            }
                        });

                        if (orphaned?.Count > 0)
                        {
                            removed += orphaned.Count;
                            meta.DeleteEntities(orphaned);
                        }
                        if (addMissing) added += AddMissing(defaultMeta, defaultFields, meta, fields);

                        meta.Repo.Events.EnsureBatch().AddMetaWithUpdatedEntities(meta.Id);
                    }
                    DebugMessage($"Values are reverted for {metaCounts} metas, {updated} entities updated, {removed} entities removed, {added} entities added");
                });
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                throw;
            }

            Finish();
        }

        public List<BGMetaEntity> GetTables(BGRepo repo)
        {
            var tableList = new List<BGMetaEntity>();

            if (ForEachToken(metas, token =>
                {
                    var meta = repo.GetMeta(token);
                    if (meta == null)
                    {
                        Debug.Log($"Can not find a meta with name={token}");
                        return;
                    }

                    tableList.Add(meta);
                }) == 0) repo.FindMetas(tableList);

            return tableList;
        }
    }
}