/*
<copyright file="BGEditorEMSingleFile.cs" company="BansheeGz">
    Copyright (c) 2019-2024 All Rights Reserved
</copyright>
*/

using System;
using System.Diagnostics;
using System.IO;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace BansheeGz.BGDatabase.Editor
{
    public class BGEditorEMSingleFile : BGDatabaseExcelImportEditorTool.Worker
    {
        private const string Key_ExcelOn = "BGDatabase.Custom.ExcelOn";
        private const string Key_ExcelFilePath = "BGDatabase.Custom.ExcelFilePath";
        private const string Key_ExcelLastSynced = "BGDatabase.Custom.ExcelLastSynced";
        private const string Key_ExcelSaveDatabase = "BGDatabase.Custom.ExcelSaveDatabase";
        private const string Key_ExcelDebug = "BGDatabase.Custom.ExcelDebug";

        private readonly BGEditorEMExecutor executor;
        private BGEditorEMFileWatcher watcher;

        private bool excelOn;
        private string excelFilePath;
        private long excelLastSynced;
        private bool excelSaveDatabase;
        private bool excelDebug;


        public bool ExcelOn
        {
            set
            {
                if (value == excelOn) return;
                BGEditorEMParameters.SetBool(Key_ExcelOn, ref excelOn, value);

                if (value) StartWatching();
                else StopWatching();
            }
        }

        public string ExcelFilePath
        {
            set
            {
                if (string.Equals(excelFilePath, value)) return;
                BGEditorEMParameters.SetString(Key_ExcelFilePath, ref excelFilePath, value);
            }
        }


        public long ExcelLastSynced
        {
            set
            {
                excelLastSynced = value;
                executor.RunOnMainThread(() => BGEditorEMParameters.SetString(Key_ExcelLastSynced, "" + value));
            }
        }

        public bool ExcelSaveDatabase
        {
            set => BGEditorEMParameters.SetBool(Key_ExcelSaveDatabase, ref excelSaveDatabase, value);
        }


        public bool ExcelDebug
        {
            set => BGEditorEMParameters.SetBool(Key_ExcelDebug, ref excelDebug, value);
        }

        public BGEditorEMSingleFile(BGEditorEMExecutor executor)
        {
            this.executor = executor;
            executor.RunOnMainThread(() =>
            {
                excelOn = BGEditorEMParameters.GetBool(Key_ExcelOn);
                excelLastSynced = BGEditorEMParameters.ReadStringAsLong(Key_ExcelLastSynced);
                excelFilePath = BGEditorEMParameters.GetString(Key_ExcelFilePath);
                excelSaveDatabase = BGEditorEMParameters.GetBool(Key_ExcelSaveDatabase);
                excelDebug = BGEditorEMParameters.GetBool(Key_ExcelDebug);

                if (excelOn) StartWatching();
            });
        }

        private void ImportExcel()
        {
            var watch = Stopwatch.StartNew();
            if (!File.Exists(excelFilePath)) throw new BGException("File does not exists, path=" + excelFilePath);
            new BGExcel(new BGLogger(), BGRepo.I, new BGMergeSettingsEntity { UpdateMatching = true }, new BGMergeSettingsMeta()).Import(
                BGSyncUtil.ReadFile(new BGLogger(), excelFilePath), false, UseXml(excelFilePath));
            if (excelSaveDatabase)
            {
                BGRepoSaver.SaveRepo();
                BGRepo.I.Events.FireAnyChange();
                if (BGRepoWindow.Active) BGRepoWindow.Instance.MarkRepoSaved();
            }

            ForEach<BGDataBinderRowGo>(go => go.Bind());
            ForEach<BGDataBinderDatabaseGo>(go => go.Bind());


            watch.Stop();
            if (excelDebug) Debug.Log("ExcelFileMonitor: executed Excel import in " + watch.Elapsed.Milliseconds + " mls.");
        }

        private static bool UseXml(string path)
        {
            if (string.IsNullOrEmpty(path)) return false;
            var extension = Path.GetExtension(path);
            return ".xlsx".Equals(extension) || "xlsx".Equals(extension);
        }


        private static void ForEach<T>(Action<T> action) where T : MonoBehaviour
        {
            var list = UnityEngine.Object.FindObjectsOfType<T>();
            if (list == null) return;
            foreach (var o in list) action(o);
        }

        public void Dispose() => StopWatching();

        private void StartWatching()
        {
            StopWatching();
            if (excelFilePath == null || !File.Exists(excelFilePath) || !excelOn) return;
            watcher = new BGEditorEMFileWatcher(excelFilePath, OnFileChanged);
        }

        private void StopWatching()
        {
            watcher?.Dispose();
            watcher = null;
        }

        // private void OnFileChanged(object sender, FileSystemEventArgs e)
        private void OnFileChanged()
        {
            // var excelFilePath = e.FullPath;
            if (!File.Exists(excelFilePath)) return;
            var modified = File.GetLastWriteTime(excelFilePath);
            if (modified.Ticks != excelLastSynced)
            {
                ExcelLastSynced = modified.Ticks;
                executor.RunOnMainThread(ImportExcel);
            }
        }

        public void Gui()
        {
            BGEditorUtility.Toggle(new GUIContent("On", "Is background monitor is on"), excelOn, v => ExcelOn = v);
            if (!excelOn) GUILayout.Label("Monitor is off", BGStyle.Attention);


            BGEditorUtility.Horizontal(() =>
            {
                BGEditorUtility.Label("File");
                ExcelFilePath = GUILayout.TextField(excelFilePath, BGStyle.Editor_textField);
                if (BGEditorUtility.Button("Open", 80))
                {
                    BGEditorUtility.OpenFile("Excel file to monitor", null, null, s => ExcelFilePath = s);
                }
            });

            BGEditorUtility.Toggle(new GUIContent("Auto-Save database", "If this setting is on, all changes to BGDatabase from Excel file will be auto-saved"),
                excelSaveDatabase,
                v => ExcelSaveDatabase = v);


            BGEditorUtility.Toggle(new GUIContent("Debug", "Turn it on to see how much time excel import has taken in the console"),
                excelDebug, v => ExcelDebug = v);

            BGEditorUtility.SwapDisabled(() =>
            {
                var fileIsSet = !string.IsNullOrEmpty(excelFilePath);
                var fileExists = fileIsSet && File.Exists(excelFilePath);

                Label("File in use :", fileIsSet ? excelFilePath : "N/A");
                Label("File exists? :", fileExists ? "Yes" : "No",
                    fileExists ? new GUIStyle { normal = { textColor = new Color(0f, 0.64f, 0.15f) } } : new GUIStyle { normal = { textColor = Color.red } });

                Label("File last modified :", !fileExists ? "N/A" : File.GetLastWriteTime(excelFilePath).ToString("MM/dd/yyyy HH:mm:ss"));
                Label("Last synced :", excelLastSynced == 0 ? "N/A" : new DateTime(excelLastSynced).ToString("MM/dd/yyyy HH:mm:ss"));
            }, !excelOn);

            BGEditorUtility.SwapDisabled(() =>
            {
                if (!BGEditorUtility.Button("Run now")) return;
                ImportExcel();
            }, excelOn);
        }
        private static void Label(string label, string message, GUIStyle style = null)
        {
            BGEditorUtility.Horizontal(() =>
            {
                BGEditorUtility.Label(label, 100);
                GUILayout.Label(message, style ?? BGStyle.Editor_label);
            });
        }
    }
}