using OllamaSharp;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows;
namespace YwxApp.AiChat.ViewModels
{
    /// 
    /// 0、Current class:
    /// 
    public class ShareOllamaObject
    {
        #region Field | Property | Collection | Command
        #region Field
        private bool _ollamaEnabled = false;        //ollama connected state
        private string _ollamaAppPath;              //ollama app path.
        private int recordIndex = 0;                //current record index.
        private string _remoteHost = "192.168.1.6";                //current record;
        private string _remotePort = "11434";                //current record;
        private string _currentPath;
        private Chat chat;                          //build interactive chat model object.
        private OllamaApiClient _ollama;            //OllamaAPI object.
        #endregion
        #region Property
        public string RemoteHost
        {
            get { return _remoteHost; }
            set { _remoteHost = value; }
        }
        public string RemotePort
        {
            get { return _remotePort; }
            set { _remotePort = value; }
        }
        public string OllamaAppPath
        {
            get { return _ollamaAppPath; }
            set { _ollamaAppPath = value; }
        }
        public bool OllamaEnabled
        {
            get { return _ollamaEnabled; }
            set { _ollamaEnabled = value; }
        }
        public OllamaApiClient Ollama
        {
            get { return _ollama; }
            set { _ollama = value; }
        }
        public Chat Chat
        {
            get { return chat; }
            set { chat = value; }
        }
        public string CurrentPath
        {
            get => _currentPath;
        }
        public int RecordIndex
        {
            get => recordIndex;
            set
            {
                recordIndex = value;
                _currentPath = $"{Environment.CurrentDirectory}//Data//{DateTime.Today.ToString("yyyyMMdd")}" +
                               $"//{DateTime.Today.ToString("yyyyMMdd")}_{recordIndex}.txt";
            }
        }
        #endregion
        #region Collection
        public ObservableCollection ModelList { get; set; }
        #endregion
        #endregion
        #region Constructor
        public ShareOllamaObject()
        {
            RecordIndex = 0;
            WriteDataToFileAsync("");
           Init(OllamaAppPath, "llama3.2:9b");
        }
        #endregion
        #region other method
        /// 
        /// initialite method
        /// 
        private void Init(string appPath, string modelName)
        {
            OllamaAppPath = appPath;
            try
            {
                // 设置默认设备为GPU
                Environment.SetEnvironmentVariable("OLLAMA_DEFAULT_DEVICE", "gpu");
                //判断路径是否存在
                if (OllamaAppPath == string.Empty || OllamaAppPath == null) OllamaAppPath = @"ollama app.exe";
                //路径存在获取应用名
                if (File.Exists(OllamaAppPath)) OllamaAppPath = Path.GetFileName(OllamaAppPath);
                //获取环境Ollama环境变量:用于找到 :ollama app.exe
                var filePath = FindExeInPath(OllamaAppPath);
                //如果路径存在,启动Ollama
                if (File.Exists(filePath)) CheckStartProcess(OllamaAppPath);
                //连接Ollama,并设置初始模型
                _ollama = new OllamaApiClient(new Uri("http://192.168.1.6:11434"));
                //获取本地可用的模型列表
                ModelList = (ObservableCollection)GetModelList();
                var tmepModelName = ModelList.FirstOrDefault(name => name.ToLower().Contains("llama3.2"));
                if (tmepModelName != null) _ollama.SelectedModel = tmepModelName;
                else if (ModelList.Count > 0) _ollama.SelectedModel = ModelList[ModelList.Count - 1];
                if (ModelList.FirstOrDefault(name => name.Equals(modelName)) != null) _ollama.SelectedModel = modelName;
                //Ollama服务启用成功
                OllamaEnabled = true;
            }
            catch (Exception)
            {
                OllamaEnabled = false;
            }
        }
        public void ConnectRemotelyOllamaServer() {
            _ollama = new OllamaApiClient(new Uri("http://192.168.1.6:11434"));
            //获取本地可用的模型列表
            ModelList = (ObservableCollection)GetModelList();
        }
        /// 
        /// update the model selected by  Ollama
        /// 
        public void UpdataSelectedModel(string model)
        {
            Ollama.SelectedModel = model;
            OllamaEnabled = true;
        }
        /// 
        /// Start Ollama app and relevant server.
        /// 
        public async void StartOllama(string appPath, string modelName)
        {
            Init(appPath, modelName); await Task.Delay(1);
        }
        /// 
        /// get model list
        /// 
        public Collection GetModelList()
        {
            var models = _ollama.ListLocalModelsAsync();
            var modelList = new ObservableCollection();
            foreach (var model in models.Result)
            {
                modelList.Add(model.Name);
            }
            return modelList;
        }
        #endregion
        #region starting or closeing method of Ollama(server).
        /// 
        /// Finds whether the specified application name is configured in the system environment. 
        /// If it exists, return the full path, otherwise return null
        /// 
        public static string FindExeInPath(string exeName)
        {
            // get environment variable "Path" value
            var pathVariable = Environment.GetEnvironmentVariable("PATH");
            // Split string
            string[] paths = pathVariable.Split(Path.PathSeparator);
            foreach (string path in paths)
            {
                string fullPath = Path.Combine(path, exeName);
                if (File.Exists(fullPath))
                {
                    return fullPath;
                }
            }
            return null;
        }
        /// 
        ///Startup program Specifies a program, enters a program name, and determines whether the program is running.
        ///     If it is running, exit directly, otherwise run the program according to the input path.
        /// 
        public static void CheckStartProcess(string processPath)
        {
            string processName = Path.GetFileName(processPath);
            CheckStartProcess(processName, processPath);
        }
        /// 
        /// Startup program Specifies a program, enters a program name, and determines whether the program is running. 
        ///     If it is running, exit directly, otherwise run the program according to the input path.
        /// 
        public static void CheckStartProcess(string processName, string processPath)
        {
            // Check whather  the program  is running 
            if (!IsProcessRunning(processName))
            {
                Console.WriteLine($"{processName} is not running. Starting the process...");
                StartProcess(processPath);
            }
            else Console.WriteLine($"{processName} is already running.");
        }
        /// 
        /// Enter the program path to start the program
        /// 
        public static void StartProcess(string processPath)
        {
            try
            {
                Process.Start(processPath);
                Console.WriteLine("Process started successfully.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error starting process: {ex.Message}");
            }
        }
        /// 
        /// Check whather the process is running
        /// 
        public static bool IsProcessRunning(string processName)
        {
            Process[] processes = Process.GetProcessesByName(processName);
            return processes.Length > 0;
        }
        /// 
        /// close the process with the specify name.
        /// 
        /// 
        public static void CloseProcess(string processName)
        {
            try
            {
                foreach (var process in Process.GetProcessesByName(processName))
                {
                    process.Kill();
                    process.WaitForExit();
                    Application.Current.Shutdown();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"无法关闭【{processName}】进程: {ex.Message}");
            }
        }
        /// 
        /// get current process name
        /// 
        public static string GetProgramName()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            return assembly.GetName().Name;
        }
        #endregion
        #region File save
        /// 
        /// Save record
        /// 
        public void WriteDataToFileAsync(string data, int retryCount = 5, int delayMilliseconds = 500)
        {
            //Get the directory where the file located.
            string directoryPath = Path.GetDirectoryName(CurrentPath);
            // if directory exists't ,create directory(include all must directory).
            if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }
            for (int i = 0; i < retryCount; i++)
            {
                try
                {
                    using (FileStream fs = new FileStream(CurrentPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                    using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8))
                    {
                        writer.WriteAsync(data);
                    }
                    return;                             // successful writed exit the loop.
                }
                catch (IOException ex)
                {
                    if (i == retryCount - 1)
                    {
                        throw;                          //If the maximum number of retries is reached , a exception is thrown
                    }
                    Task.Delay(delayMilliseconds);    // Wait a while and try again
                }
                catch (Exception ex)
                {
                    throw;                              //other exception is thrown
                }
            }
        }
        #endregion
    }
}