// Alyios Automation Studio - Script Engine
// Playback, export, and script management

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using AlyiosAutomation.Models;

namespace AlyiosAutomation.Engine
{
    // Automation script
    public class AutomationScript
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public List<IAutomationAction> Actions { get; set; }
        public Dictionary<string, object> Metadata { get; set; }

        public AutomationScript()
        {
            Actions = new List<IAutomationAction>();
            Metadata = new Dictionary<string, object>();
            Metadata["created"] = DateTime.Now;
            Metadata["version"] = "1.0";
        }

        public void SaveToFile(string filePath)
        {
            var data = new Dictionary<string, object>
            {
                {"name", Name},
                {"description", Description},
                {"metadata", Metadata},
                {"actions", Actions.Select(a => a.Serialize()).ToList()}
            };

            string json = JsonConvert.SerializeObject(data, Formatting.Indented);
            File.WriteAllText(filePath, json);
        }

        public static AutomationScript LoadFromFile(string filePath)
        {
            var json = File.ReadAllText(filePath);
            var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);

            var script = new AutomationScript();
            if (data.ContainsKey("name")) script.Name = data["name"].ToString();
            if (data.ContainsKey("description")) script.Description = data["description"].ToString();
            if (data.ContainsKey("metadata"))
            {
                var metadataJson = JsonConvert.SerializeObject(data["metadata"]);
                script.Metadata = JsonConvert.DeserializeObject<Dictionary<string, object>>(metadataJson);
            }

            if (data.ContainsKey("actions"))
            {
                var actionsJson = JsonConvert.SerializeObject(data["actions"]);
                var actions = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(actionsJson);
                foreach (var actionDict in actions)
                {
                    var action = ActionFactory.Deserialize(actionDict);
                    if (action != null)
                    {
                        script.Actions.Add(action);
                    }
                }
            }

            return script;
        }

        public string ExportToPython()
        {
            var sb = new StringBuilder();
            sb.AppendLine("# Generated by Alyios Automation Studio");
            sb.AppendLine("# " + DateTime.Now.ToString());
            sb.AppendLine();
            sb.AppendLine("from AlyiosWindowsFunctions import click, move_mouse, press_key, type_text, send_keys");
            sb.AppendLine("import time");
            sb.AppendLine();
            sb.AppendLine("def run_automation():");

            foreach (var action in Actions)
            {
                sb.AppendLine("    " + ActionToPython(action));
            }

            sb.AppendLine();
            sb.AppendLine("if __name__ == '__main__':");
            sb.AppendLine("    run_automation()");

            return sb.ToString();
        }

        string ActionToPython(IAutomationAction action)
        {
            if (action is ClickAction)
            {
                var click = (ClickAction)action;
                return string.Format("click({0}, {1}, button='{2}')",
                    click.X, click.Y, click.Button.ToLower());
            }
            else if (action is TypeTextAction)
            {
                var type = (TypeTextAction)action;
                return string.Format("type_text(\"{0}\")",
                    type.Text.Replace("\"", "\\\""));
            }
            else if (action is PressKeyAction)
            {
                var key = (PressKeyAction)action;
                if (key.Modifiers.Count > 0)
                {
                    string combo = string.Join("+", key.Modifiers.Select(m => m.ToLower())) +
                                   "+" + key.Key.ToLower();
                    return string.Format("send_keys('{0}')", combo);
                }
                return string.Format("press_key('{0}')", key.Key.ToLower());
            }
            else if (action is DelayAction)
            {
                var delay = (DelayAction)action;
                return string.Format("time.sleep({0})", delay.Milliseconds / 1000.0);
            }

            return "# Unsupported action: " + action.GetType().Name;
        }
    }

    // Script player
    public class ScriptPlayer
    {
        public event Action<string> OnLog;
        public event Action<int, int> OnProgress;
        public event Action OnComplete;
        public event Action<string> OnError;

        bool shouldStop = false;
        bool isPaused = false;

        public async Task PlayAsync(AutomationScript script, int loopCount = 1, double speedMultiplier = 1.0)
        {
            shouldStop = false;
            var context = new AutomationContext();

            await Task.Run(() =>
            {
                for (int loop = 0; loop < loopCount && !shouldStop; loop++)
                {
                    Log(string.Format("Starting loop {0}/{1}", loop + 1, loopCount));

                    for (int i = 0; i < script.Actions.Count && !shouldStop; i++)
                    {
                        while (isPaused && !shouldStop)
                        {
                            Thread.Sleep(100);
                        }

                        if (shouldStop) break;

                        var action = script.Actions[i];

                        try
                        {
                            if (action is DelayAction)
                            {
                                int delay = (int)(((DelayAction)action).Milliseconds * speedMultiplier);
                                Thread.Sleep(delay);
                            }
                            else
                            {
                                action.Execute(context);
                            }

                            UpdateProgress(i + 1, script.Actions.Count);
                        }
                        catch (Exception ex)
                        {
                            Error(string.Format("Error executing {0}: {1}",
                                action.Name, ex.Message));
                            context.ShouldStop = true;
                            break;
                        }
                    }

                    if (context.ShouldStop) break;
                }

                Complete();
            });
        }

        public void Stop()
        {
            shouldStop = true;
            isPaused = false;
        }

        public void Pause()
        {
            isPaused = true;
        }

        public void Resume()
        {
            isPaused = false;
        }

        void Log(string message)
        {
            if (OnLog != null) OnLog(message);
        }

        void UpdateProgress(int current, int total)
        {
            if (OnProgress != null) OnProgress(current, total);
        }

        void Complete()
        {
            if (OnComplete != null) OnComplete();
        }

        void Error(string message)
        {
            if (OnError != null) OnError(message);
        }
    }

    // Script recorder
    public class ScriptRecorder
    {
        AutomationScript script;
        DateTime lastActionTime;
        int minDelayMs = 100;

        public event Action<IAutomationAction> OnActionRecorded;

        public ScriptRecorder()
        {
            script = new AutomationScript();
        }

        public void Start(string scriptName = "New Script")
        {
            script = new AutomationScript
            {
                Name = scriptName,
                Description = "Recorded on " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
            };
            lastActionTime = DateTime.Now;
        }

        public void RecordClick(int x, int y, string button)
        {
            AddDelayIfNeeded();

            var action = new ClickAction
            {
                X = x,
                Y = y,
                Button = button,
                ClickCount = 1
            };

            script.Actions.Add(action);
            lastActionTime = DateTime.Now;

            if (OnActionRecorded != null)
                OnActionRecorded(action);
        }

        public void RecordKeyPress(string key, List<string> modifiers = null)
        {
            AddDelayIfNeeded();

            var action = new PressKeyAction
            {
                Key = key,
                Modifiers = modifiers ?? new List<string>()
            };

            script.Actions.Add(action);
            lastActionTime = DateTime.Now;

            if (OnActionRecorded != null)
                OnActionRecorded(action);
        }

        public void RecordTypeText(string text)
        {
            AddDelayIfNeeded();

            var action = new TypeTextAction { Text = text };
            script.Actions.Add(action);
            lastActionTime = DateTime.Now;

            if (OnActionRecorded != null)
                OnActionRecorded(action);
        }

        public void AddDelay(int milliseconds)
        {
            var action = new DelayAction { Milliseconds = milliseconds };
            script.Actions.Add(action);

            if (OnActionRecorded != null)
                OnActionRecorded(action);
        }

        void AddDelayIfNeeded()
        {
            if (script.Actions.Count == 0) return;

            var elapsed = (int)(DateTime.Now - lastActionTime).TotalMilliseconds;
            if (elapsed > minDelayMs)
            {
                AddDelay(elapsed);
            }
        }

        public AutomationScript GetScript()
        {
            return script;
        }

        public void Clear()
        {
            script = new AutomationScript();
        }
    }
}
