using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using AR;

namespace Project.WebUI
{
    // Important: Allows JavaScript to see this class
    [ClassInterface(ClassInterfaceType.AutoDual)]
    [ComVisible(true)]
    public class MachineBridge
    {
        // Reference to the main form to update logic
        private Project.Dialog.fWebView _host;

        // Data folder paths
        private readonly string _dataFolder;
        private readonly string _settingsPath;
        private readonly string _recipeFolder;

        public MachineBridge(Project.Dialog.fWebView host)
        {
            _host = host;

            // Initialize data folder paths - use existing Data folder
            _dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data");
            _settingsPath = Path.Combine(_dataFolder, "Setting.json");
            _recipeFolder = Path.Combine(_dataFolder, "recipe");

            // Ensure folders exist
            EnsureDataFoldersExist();
        }

        private void EnsureDataFoldersExist()
        {
            try
            {
                if (!Directory.Exists(_dataFolder))
                    Directory.CreateDirectory(_dataFolder);

                if (!Directory.Exists(_recipeFolder))
                    Directory.CreateDirectory(_recipeFolder);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to create data folders: {ex.Message}");
            }
        }

        // --- Methods called from React ---

        public void MoveAxis(string axis, double value)
        {
            Console.WriteLine($"[C#] Moving {axis} to {value}");
            _host.SetTargetPosition(axis, value);
        }

        public void SetIO(int id, bool isInput, bool state)
        {
            Console.WriteLine($"[C#] Set IO {id} to {state}");
            _host.SetOutput(id, state);
        }

        public void SystemControl(string command)
        {
            Console.WriteLine($"[C#] CMD: {command}");
            _host.HandleCommand(command);
        }

        public string SelectRecipe(string recipeId)
        {
            Console.WriteLine($"[C#] Selecting Recipe: {recipeId}");

            try
            {
                string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json");

                if (!File.Exists(recipePath))
                {
                    var response = new { success = false, message = "Recipe not found" };
                    return JsonConvert.SerializeObject(response);
                }

                string json = File.ReadAllText(recipePath);
                var recipeData = JsonConvert.DeserializeObject<dynamic>(json);

                _host.SetCurrentRecipe(recipeId);

                Console.WriteLine($"[INFO] Recipe {recipeId} selected successfully");

                var response2 = new { success = true, message = "Recipe selected successfully", recipeId = recipeId, recipe = recipeData };
                return JsonConvert.SerializeObject(response2);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to select recipe: {ex.Message}");
                var response = new { success = false, message = ex.Message };
                return JsonConvert.SerializeObject(response);
            }
        }

        /// <summary>
        /// ���̱׷��̼� ����
        /// </summary>
        /// <returns></returns>
        public string GetConfig()
        {
            try
            {
                // Use SETTING.Data (CommonSetting) from the project
                if (AR.SETTING.Data == null)
                {
                    Console.WriteLine($"[WARN] SETTING.Data is not initialized");
                    return "[]";
                }

                var settingsArray = new List<object>();
                var properties = AR.SETTING.Data.GetType().GetProperties();

                foreach (var prop in properties)
                {
                    // Skip non-browsable properties
                    var browsable = prop.GetCustomAttributes(typeof(System.ComponentModel.BrowsableAttribute), false)
                        .FirstOrDefault() as System.ComponentModel.BrowsableAttribute;
                    if (browsable != null && !browsable.Browsable)
                        continue;

                    // Skip filename property
                    if (prop.Name == "filename")
                        continue;

                    // Get property info
                    string key = prop.Name;
                    object value = prop.GetValue(AR.SETTING.Data, null);
                    string type = "String";
                    string group = "General";
                    string description = key.Replace("_", " ");

                    // Get Category attribute
                    var categoryAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.CategoryAttribute), false)
                        .FirstOrDefault() as System.ComponentModel.CategoryAttribute;
                    if (categoryAttr != null)
                        group = categoryAttr.Category;

                    // Get DisplayName attribute
                    var displayNameAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.DisplayNameAttribute), false)
                        .FirstOrDefault() as System.ComponentModel.DisplayNameAttribute;
                    if (displayNameAttr != null)
                        description = displayNameAttr.DisplayName;

                    // Get Description attribute
                    var descAttr = prop.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false)
                        .FirstOrDefault() as System.ComponentModel.DescriptionAttribute;
                    if (descAttr != null)
                        description = descAttr.Description;

                    // Determine type based on property type
                    if (prop.PropertyType == typeof(bool))
                    {
                        type = "Boolean";
                        value = value?.ToString().ToLower() ?? "false";
                    }
                    else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(long) ||
                             prop.PropertyType == typeof(double) || prop.PropertyType == typeof(float))
                    {
                        type = "Number";
                        value = value?.ToString() ?? "0";
                    }
                    else
                    {
                        value = value?.ToString() ?? "";
                    }

                    settingsArray.Add(new
                    {
                        Key = key,
                        Value = value,
                        Group = group,
                        Type = type,
                        Description = description
                    });
                }

                Console.WriteLine($"[INFO] Loaded {settingsArray.Count} settings from SETTING.Data");
                return JsonConvert.SerializeObject(settingsArray);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to load settings: {ex.Message}");
                return "[]";
            }
        }

        /// <summary>
        /// ���̱׷��̼Ǽ���
        /// </summary>
        /// <param name="configJson"></param>
        public void SaveConfig(string configJson)
        {
            try
            {
                Console.WriteLine($"[Backend] SAVE CONFIG REQUEST RECEIVED");

                // Parse array format from React
                var settingsArray = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(configJson);

                // Update SETTING.Data properties
                var properties = AR.SETTING.Data.GetType().GetProperties();

                foreach (var item in settingsArray)
                {
                    string key = item["Key"].ToString();
                    string value = item["Value"].ToString();
                    string type = item["Type"].ToString();

                    var prop = properties.FirstOrDefault(p => p.Name == key);
                    if (prop == null || !prop.CanWrite)
                        continue;

                    // Convert value based on type
                    try
                    {
                        if (type == "Boolean")
                        {
                            prop.SetValue(AR.SETTING.Data, bool.Parse(value), null);
                        }
                        else if (type == "Number")
                        {
                            if (prop.PropertyType == typeof(int))
                                prop.SetValue(AR.SETTING.Data, int.Parse(value), null);
                            else if (prop.PropertyType == typeof(long))
                                prop.SetValue(AR.SETTING.Data, long.Parse(value), null);
                            else if (prop.PropertyType == typeof(double))
                                prop.SetValue(AR.SETTING.Data, double.Parse(value), null);
                            else if (prop.PropertyType == typeof(float))
                                prop.SetValue(AR.SETTING.Data, float.Parse(value), null);
                        }
                        else
                        {
                            prop.SetValue(AR.SETTING.Data, value, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"[WARN] Failed to set property {key}: {ex.Message}");
                    }
                }

                // Save to file
                AR.SETTING.Data.Save();

                Console.WriteLine($"[INFO] Settings saved successfully");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to save settings: {ex.Message}");
            }
        }

        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <returns></returns>
        public string GetIOList()
        {
            try
            {
                var result = new
                {
                    inputs = GetDigitalInputs(),
                    outputs = GetDigitalOutputs(),
                    interlocks = GetInterlocks()
                };

                return JsonConvert.SerializeObject(result);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get IO list: {ex.Message}");
                return JsonConvert.SerializeObject(new { inputs = new List<object>(), outputs = new List<object>(), interlocks = new List<object>() });
            }
        }
        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <returns></returns>
        private List<object> GetDigitalInputs()
        {
            var inputs = new List<object>();

            try
            {
                var diNames = DIO.Pin.GetDIName;
                var diPinNames = DIO.Pin.GetDIPinName;
                int diCount = PUB.dio.GetDICount;

                for (int i = 0; i < diCount; i++)
                {
                    bool state = PUB.dio.GetDIValue(i);
                    string name = i < diNames.Length ? diNames[i] : $"DIN_{i:D2}";
                    string pinName = i < diPinNames.Length ? diPinNames[i] : $"X{i:D2}";

                    inputs.Add(new
                    {
                        id = i,
                        name = name,
                        pinName = pinName,
                        type = "input",
                        state = state
                    });
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get digital inputs: {ex.Message}");
            }

            return inputs;
        }
        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <returns></returns>
        private List<object> GetDigitalOutputs()
        {
            var outputs = new List<object>();

            try
            {
                var doNames = DIO.Pin.GetDOName;
                var doPinNames = DIO.Pin.GetDOPinName;
                int doCount = PUB.dio.GetDOCount;

                for (int i = 0; i < doCount; i++)
                {
                    bool state = PUB.dio.GetDOValue(i);
                    string name = i < doNames.Length ? doNames[i] : $"DOUT_{i:D2}";
                    string pinName = i < doPinNames.Length ? doPinNames[i] : $"Y{i:D2}";

                    outputs.Add(new
                    {
                        id = i,
                        name = name,
                        pinName = pinName,
                        type = "output",
                        state = state
                    });
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get digital outputs: {ex.Message}");
            }

            return outputs;
        }
        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <returns></returns>
        private List<object> GetInterlocks()
        {
            var interlocks = new List<object>();

            try
            {
                for (int i = 0; i < PUB.iLock.Length; i++)
                {
                    var axisName = ((AR.eAxis)i).ToString();
                    var nonAxis = false;

                    // Check if this is a non-axis interlock (i >= 7)
                    if (i >= 7)
                    {
                        axisName = PUB.iLock[i].Tag.ToString();
                        nonAxis = true;
                    }

                    // Get enum names based on axis index
                    string[] ilockNames;
                    if (i == 7) ilockNames = Enum.GetNames(typeof(eILockPRL));
                    else if (i == 8) ilockNames = Enum.GetNames(typeof(eILockPRR));
                    else if (i == 9) ilockNames = Enum.GetNames(typeof(eILockVS0));
                    else if (i == 10) ilockNames = Enum.GetNames(typeof(eILockVS1));
                    else if (i == 11) ilockNames = Enum.GetNames(typeof(eILockVS2));
                    else if (i == 12 || i == 13) ilockNames = Enum.GetNames(typeof(eILockCV));
                    else ilockNames = Enum.GetNames(typeof(eILock));

                    // Get interlock values (up to 64 bits)
                    var lockValues = new List<object>();
                    for (int j = 0; j < ilockNames.Length && j < 64; j++)
                    {
                        bool state = PUB.iLock[i].get(j);
                        lockValues.Add(new
                        {
                            id = j,
                            name = ilockNames[j],
                            state = state
                        });
                    }

                    interlocks.Add(new
                    {
                        axisIndex = i,
                        axisName = axisName,
                        nonAxis = nonAxis,
                        locks = lockValues,
                        hexValue = PUB.iLock[i].Value().HexString()
                    });
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get interlocks: {ex.Message}");
            }

            return interlocks;
        }


        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <returns></returns>
        public string GetRecipeList()
        {
            //PUB.mdm.dataSet.OPModel에 등록된 데이터가 모델 목록이다

            try
            {
                var recipes = new List<object>();

                if (PUB.mdm == null || PUB.mdm.dataSet == null || PUB.mdm.dataSet.OPModel == null)
                {
                    Console.WriteLine($"[WARN] OPModel is not initialized");
                    return JsonConvert.SerializeObject(recipes);
                }

                // OPModel 테이블에서 Title이 비어있지 않은 항목만 가져오기
                foreach (var row in PUB.mdm.dataSet.OPModel)
                {
                    var modelRow = row as DataSet1.OPModelRow;
                    if (modelRow == null) continue;

                    // Title이 비어있으면 스킵
                    if (string.IsNullOrEmpty(modelRow.Title))
                        continue;

                    recipes.Add(new
                    {
                        id = modelRow.idx.ToString(),
                        name = modelRow.Title,
                        lastModified = DateTime.Now.ToString("yyyy-MM-dd")
                    });
                }

                Console.WriteLine($"[INFO] Loaded {recipes.Count} recipes from OPModel");
                return JsonConvert.SerializeObject(recipes);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get recipe list: {ex.Message}");
                return "[]";
            }
        }

        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <param name="recipeTitle"></param>
        /// <returns></returns>
        public string GetRecipe(string recipeTitle)
        {
            try
            {
                var dr = PUB.mdm.GetDataV(recipeTitle);

                if (dr == null)
                {
                    var response = new { success = false, message = "Recipe not found" };
                    return JsonConvert.SerializeObject(response);
                }

                // DataRow의 모든 컬럼을 Dictionary로 변환
                var recipeData = new Dictionary<string, object>();
                foreach (System.Data.DataColumn col in dr.Table.Columns)
                {
                    string colName = col.ColumnName;
                    object value = dr[colName];

                    // DBNull을 null로 변환
                    if (value == DBNull.Value)
                        value = null;

                    recipeData[colName] = value;
                }

                Console.WriteLine($"[INFO] Loaded recipe {recipeTitle}");
                return JsonConvert.SerializeObject(recipeData);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to get recipe {recipeTitle}: {ex.Message}");
                var response = new { success = false, message = ex.Message };
                return JsonConvert.SerializeObject(response);
            }
        }

        /// <summary>
        /// 마이그레이션완료
        /// </summary>
        /// <param name="recipeTitle"></param>
        /// <param name="recipeData"></param>
        /// <returns></returns>
        public string SaveRecipe(string recipeTitle, string recipeData)
        {
            try
            {
                var dr = PUB.mdm.GetDataV(recipeTitle);

                if (dr == null)
                {
                    var response = new { success = false, message = "Recipe not found" };
                    return JsonConvert.SerializeObject(response);
                }

                // JSON 데이터를 Dictionary로 변환
                var recipeDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(recipeData);

                // DataRow의 각 컬럼 업데이트
                foreach (var kvp in recipeDict)
                {
                    string colName = kvp.Key;

                    // 컬럼이 테이블에 존재하는지 확인
                    if (!dr.Table.Columns.Contains(colName))
                        continue;

                    // idx나 Midx 같은 자동 생성 컬럼은 스킵
                    if (colName == "idx" || colName == "Midx")
                        continue;

                    try
                    {
                        var col = dr.Table.Columns[colName];
                        object value = kvp.Value;

                        // null 처리
                        if (value == null)
                        {
                            dr[colName] = DBNull.Value;
                        }
                        else
                        {
                            // 타입 변환
                            if (col.DataType == typeof(bool))
                                dr[colName] = Convert.ToBoolean(value);
                            else if (col.DataType == typeof(int))
                                dr[colName] = Convert.ToInt32(value);
                            else if (col.DataType == typeof(double))
                                dr[colName] = Convert.ToDouble(value);
                            else if (col.DataType == typeof(float))
                                dr[colName] = Convert.ToSingle(value);
                            else
                                dr[colName] = value.ToString();
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"[WARN] Failed to set column {colName}: {ex.Message}");
                    }
                }

                // 변경사항 저장
                PUB.mdm.dataSet.OPModel.AcceptChanges();
                PUB.mdm.SaveModelV();

                Console.WriteLine($"[INFO] Recipe {recipeTitle} saved successfully to OPModel");

                var response2 = new { success = true, message = "Recipe saved successfully", recipeId = recipeTitle };
                return JsonConvert.SerializeObject(response2);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to save recipe {recipeTitle}: {ex.Message}");
                var response = new { success = false, message = ex.Message };
                return JsonConvert.SerializeObject(response);
            }
        }

        public string CopyRecipe(string recipeId, string newName)
        {
            Console.WriteLine($"[C#] Copying Recipe: {recipeId} as {newName}");

            try
            {
                string sourcePath = Path.Combine(_recipeFolder, $"{recipeId}.json");

                if (!File.Exists(sourcePath))
                {
                    var response = new { success = false, message = "Source recipe not found" };
                    return JsonConvert.SerializeObject(response);
                }

                string newId = Guid.NewGuid().ToString().Substring(0, 8);
                string destPath = Path.Combine(_recipeFolder, $"{newId}.json");

                string json = File.ReadAllText(sourcePath);
                var recipeData = JsonConvert.DeserializeObject<dynamic>(json);

                recipeData.name = newName;

                File.WriteAllText(destPath, JsonConvert.SerializeObject(recipeData, Formatting.Indented));

                string timestamp = DateTime.Now.ToString("yyyy-MM-dd");

                Console.WriteLine($"[INFO] Recipe copied from {recipeId} to {newId}");

                var response2 = new {
                    success = true,
                    message = "Recipe copied successfully",
                    newRecipe = new {
                        id = newId,
                        name = newName,
                        lastModified = timestamp
                    }
                };
                return JsonConvert.SerializeObject(response2);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to copy recipe: {ex.Message}");
                var response = new { success = false, message = ex.Message };
                return JsonConvert.SerializeObject(response);
            }
        }

        public string DeleteRecipe(string recipeId)
        {
            Console.WriteLine($"[C#] Deleting Recipe: {recipeId}");

            try
            {
                if (recipeId == _host.GetCurrentRecipe())
                {
                    var response1 = new { success = false, message = "Cannot delete currently selected recipe" };
                    return JsonConvert.SerializeObject(response1);
                }

                string recipePath = Path.Combine(_recipeFolder, $"{recipeId}.json");

                if (!File.Exists(recipePath))
                {
                    var response = new { success = false, message = "Recipe not found" };
                    return JsonConvert.SerializeObject(response);
                }

                File.Delete(recipePath);

                Console.WriteLine($"[INFO] Recipe {recipeId} deleted successfully");

                var response2 = new { success = true, message = "Recipe deleted successfully", recipeId = recipeId };
                return JsonConvert.SerializeObject(response2);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to delete recipe: {ex.Message}");
                var response = new { success = false, message = ex.Message };
                return JsonConvert.SerializeObject(response);
            }
        }
    }
}
