using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XiaoZhiSharp.Kernels;
using XiaoZhiSharp.Protocols;
using XiaoZhiSharp.Utils;

namespace XiaoZhiSharp.Services.Chat
{
    public class ChatService : IDisposable
    {
        private static AsyncSemaphore s_asyncSemaphore = new AsyncSemaphore();

        private string TAG = "小智";
        private string _wsUrl { get; set; } = "wss://api.tenclass.net/xiaozhi/v1/";
        private string? _token { get; set; } = "test-token";
        private string? _deviceId { get; set; }
        private string? _sessionId = "";
        // 首次连接
        private bool _isFirst = true;
        private ClientWebSocket? _webSocket = null;
        private bool _disposed = false;
        private System.Timers.Timer _onAudioTimeout;

        #region 属性
        public WebSocketState ConnectState { get { return _webSocket?.State ?? WebSocketState.None; } }
        #endregion

        #region 事件
        public delegate Task MessageEventHandler(string type, string message);
        public event MessageEventHandler? OnMessageEvent = null;

        public delegate Task AudioEventHandler(byte[] opus);
        public event AudioEventHandler? OnAudioEvent = null;
        #endregion

        #region 构造函数
        public ChatService(string wsUrl, string token, string deviceId)
        {
            _wsUrl = wsUrl;
            _token = token;
            _deviceId = deviceId;
        }
        #endregion

        public void Start()
        {
            Uri uri = new Uri(_wsUrl);
            _webSocket = new ClientWebSocket();
            _webSocket.Options.SetRequestHeader("Authorization", "Bearer " + _token);
            _webSocket.Options.SetRequestHeader("Protocol-Version", "1");
            _webSocket.Options.SetRequestHeader("Device-Id", _deviceId);
            _webSocket.Options.SetRequestHeader("Client-Id", Guid.NewGuid().ToString());
            _webSocket.ConnectAsync(uri, CancellationToken.None);
            LogConsole.InfoLine($"{TAG} 连接中...");

            Task.Run(async () =>
            {
                await ReceiveMessagesAsync();
            });

            // 语音合成播报超时
            _onAudioTimeout = new System.Timers.Timer(500);
            _onAudioTimeout.Elapsed += async (sender, e) => await OnAudioTimeout();
            _onAudioTimeout.AutoReset = false;
        }

        /// <summary>
        /// 语音播报完成
        /// </summary>
        private async Task OnAudioTimeout()
        {
            if (OnMessageEvent != null)
                await OnMessageEvent("audio_stop", "");
        }

        private async Task ReceiveMessagesAsync()
        {
            if (_webSocket == null)
                return;

            var buffer = new byte[1024 * 15];
            while (true)
            {
                using (await s_asyncSemaphore.WaitAsync())
                {
                    if (_webSocket != null && _webSocket.State == WebSocketState.Open)
                    {
                        try
                        {
                            // 首次
                            if (_isFirst)
                            {
                                _isFirst = false;
                                LogConsole.InfoLine($"{TAG} 连接成功");
                                await SendMessageAsync(XiaoZhi_Protocol.Hello(Global.IsMcp));
                            }

                            var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                            byte[] messageBytes = new byte[result.Count];
                            Array.Copy(buffer, messageBytes, result.Count);

                            if (result.MessageType == WebSocketMessageType.Text)
                            {
                                var message = Encoding.UTF8.GetString(messageBytes);
                                LogConsole.ReceiveLine($"{TAG} {message}");

                                if (!string.IsNullOrEmpty(message))
                                {
                                    dynamic? msg = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(message);
                                    if (msg == null)
                                    {
                                        LogConsole.ErrorLine($"{TAG} 接收到的消息格式错误: {message}");
                                        continue;
                                    }
                                    _sessionId = msg.session_id;
                                    if (msg.type == "mcp")
                                    {
                                        if (OnMessageEvent != null)
                                            await OnMessageEvent("mcp", Newtonsoft.Json.JsonConvert.SerializeObject(msg.payload));
                                    }
                                    // 问
                                    if (msg.type == "stt")
                                    {
                                        if (OnMessageEvent != null)
                                            await OnMessageEvent("question", System.Convert.ToString(msg.text));

                                        if (OnMessageEvent != null)
                                            await OnMessageEvent("audio_start", "");
                                    }
                                    // 答
                                    if (msg.type == "tts")
                                    {
                                        if (msg.state == "sentence_start")
                                        {
                                            if (OnMessageEvent != null)
                                                await OnMessageEvent("answer", System.Convert.ToString(msg.text));
                                        }

                                        if (msg.state == "stop")
                                        {
                                            if (OnMessageEvent != null)
                                                await OnMessageEvent("answer_stop", "");
                                        }
                                    }
                                    // 情感
                                    if (msg.type == "llm")
                                    {
                                        if (OnMessageEvent != null)
                                            await OnMessageEvent("emotion", System.Convert.ToString(msg.emotion));
                                        if (OnMessageEvent != null)
                                            await OnMessageEvent("emotion_text", System.Convert.ToString(msg.text));
                                    }
                                }

                            }

                            if (result.MessageType == WebSocketMessageType.Binary)
                            {
                                _onAudioTimeout.Stop();
                                // 触发事件
                                if (OnAudioEvent != null)
                                    await OnAudioEvent(messageBytes);
                                _onAudioTimeout.Start();
                            }
                        }
                        catch (Exception ex)
                        {
                            LogConsole.ErrorLine($"{TAG} {ex.Message}");
                        }
                    }
                }
                Thread.Sleep(1); // 避免过于频繁的循环
            }
        }
        private async Task SendMessageAsync(string message)
        {
            if (_webSocket == null)
                return;

            if (_webSocket.State == WebSocketState.Open)
            {
                var buffer = Encoding.UTF8.GetBytes(message);
                await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
                LogConsole.SendLine($"{TAG} {message}");
            }
        }
        private async Task SendAudioAsync(byte[] opus)
        {
            if (_webSocket == null)
                return;

            if (_webSocket.State == WebSocketState.Open)
            {
                await _webSocket.SendAsync(new ArraySegment<byte>(opus), WebSocketMessageType.Binary, true, CancellationToken.None);
            }
        }
        public async Task SendAudio(byte[] audio)
        {
            await SendAudioAsync(audio);
        }
        public async Task ChatAbort()
        {
            await SendMessageAsync(XiaoZhi_Protocol.Abort());
        }
        public async Task ChatMessage(string message)
        {
            //await ChatAbort();
            await SendMessageAsync(XiaoZhi_Protocol.Listen_Detect(message));
        }
        public async Task McpMessage(string message)
        {
            await SendMessageAsync(XiaoZhi_Protocol.Mcp(message, _sessionId));
        }
        public async Task StartRecording()
        {
            //await ChatAbort();
            await SendMessageAsync(XiaoZhi_Protocol.Listen_Start("", "manual"));
        }
        public async Task StartRecordingAuto()
        {
            //await ChatAbort();
            await SendMessageAsync(XiaoZhi_Protocol.Listen_Start("", "auto"));
        }
        public async Task StopRecording()
        {
            await SendMessageAsync(XiaoZhi_Protocol.Listen_Stop(_sessionId));
        }
        public void Dispose()
        {
            if (!_disposed)
            {
                _disposed = true;

                // 关闭WebSocket连接
                try
                {
                    if (_webSocket != null && (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting))
                    {
                        _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closing", CancellationToken.None).Wait();
                    }
                }
                catch (Exception ex)
                {
                    LogConsole.ErrorLine($"{TAG} 关闭WebSocket连接时出错: {ex.Message}");
                }

                _webSocket?.Dispose();
                _webSocket = null;

                _onAudioTimeout?.Dispose();
            }
        }
    }
}
