using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace NoticeServer
{
    /// <summary>
    /// TCP 채팅 서버
    /// </summary>
    public class TcpChatServer
    {
        private TcpListener tcpListener;
        private Thread acceptThread;
        private bool isRunning;
        private int tcpPort;
        private int maxClients;

        private readonly object clientsLock = new object();
        private List<ChatClient> clients = new List<ChatClient>();

        public TcpChatServer(int tcpPort, int maxClients = 100)
        {
            this.tcpPort = tcpPort;
            this.maxClients = maxClients;
        }

        /// <summary>
        /// TCP 채팅 서버 시작
        /// </summary>
        public void Start()
        {
            if (isRunning)
                return;

            try
            {
                tcpListener = new TcpListener(IPAddress.Any, tcpPort);
                tcpListener.Start();
                isRunning = true;

                acceptThread = new Thread(AcceptLoop)
                {
                    IsBackground = true,
                    Name = "TcpAcceptThread"
                };
                acceptThread.Start();

                Console.WriteLine($"[TCP] Chat server started (Port: {tcpPort})");
                Console.WriteLine($"[TCP] Max clients: {maxClients}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[ERROR] Failed to start TCP server: {ex.Message}");
                isRunning = false;
            }
        }

        /// <summary>
        /// 클라이언트 연결 수락 루프
        /// </summary>
        private void AcceptLoop()
        {
            while (isRunning)
            {
                try
                {
                    TcpClient tcpClient = tcpListener.AcceptTcpClient();

                    lock (clientsLock)
                    {
                        if (clients.Count >= maxClients)
                        {
                            Console.WriteLine("[REJECTED] Maximum clients exceeded");
                            tcpClient.Close();
                            continue;
                        }
                    }

                    var client = new ChatClient(tcpClient);
                    client.MessageReceived += OnClientMessageReceived;
                    client.Disconnected += OnClientDisconnected;

                    lock (clientsLock)
                    {
                        clients.Add(client);
                    }

                    Console.WriteLine($"[CONNECTED] New client: {client.IpAddress} (ID: {client.ClientId})");
                }
                catch (SocketException)
                {
                    // Server shutdown
                    break;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"[ERROR] Client accept error: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// 클라이언트 메시지 수신 이벤트 핸들러
        /// </summary>
        private void OnClientMessageReceived(object sender, ChatMessage message)
        {
            var client = sender as ChatClient;
            if (client == null)
                return;

            Console.WriteLine($"[RECEIVED] {client.NickName ?? client.IpAddress}: {message.Type} - {message.Content}");

            switch (message.Type)
            {
                case MessageType.Connect:
                    HandleConnect(client, message);
                    break;

                case MessageType.Chat:
                    HandleChat(client, message);
                    break;

                case MessageType.Whisper:
                    HandleWhisper(client, message);
                    break;

                case MessageType.UserListRequest:
                    HandleUserListRequest(client);
                    break;

                case MessageType.Ping:
                    HandlePing(client);
                    break;

                case MessageType.Disconnect:
                    client.Disconnect();
                    break;
            }
        }

        /// <summary>
        /// 연결 처리
        /// </summary>
        private void HandleConnect(ChatClient client, ChatMessage message)
        {
            // Check employee ID duplication
            lock (clientsLock)
            {
                if (!string.IsNullOrEmpty(message.EmployeeId) &&
                    clients.Any(c => c.ClientId != client.ClientId && c.EmployeeId == message.EmployeeId))
                {
                    var errorMsg = new ChatMessage
                    {
                        Type = MessageType.Notice,
                        Content = "Employee ID already connected.",
                        Timestamp = DateTime.Now
                    };
                    client.SendMessage(errorMsg);
                    client.Disconnect();
                    return;
                }
            }

            Console.WriteLine($"[LOGIN] {message.NickName} ({message.EmployeeId}, {message.UserGroup}) from {client.IpAddress}, {message.HostName}");

            // Welcome message
            var welcomeMsg = new ChatMessage
            {
                Type = MessageType.Notice,
                Content = $"Welcome to the chat server, {message.NickName}!",
                Timestamp = DateTime.Now
            };
            client.SendMessage(welcomeMsg);

            // Notify other users
            var noticeMsg = new ChatMessage
            {
                Type = MessageType.Notice,
                Content = $"{message.NickName} ({message.EmployeeId}) has joined the chat.",
                Timestamp = DateTime.Now
            };
            BroadcastMessage(noticeMsg, client.ClientId);
        }

        /// <summary>
        /// 채팅 메시지 처리 (1:1 messaging)
        /// </summary>
        private void HandleChat(ChatClient client, ChatMessage message)
        {
            // Set sender information
            message.NickName = client.NickName;
            message.EmployeeId = client.EmployeeId;
            message.UserGroup = client.UserGroup;
            message.IpAddress = client.IpAddress;
            message.HostName = client.HostName;
            message.Timestamp = DateTime.Now;

            // Send to target recipient (1:1 messaging)
            if (!string.IsNullOrEmpty(message.TargetEmployeeId))
            {
                lock (clientsLock)
                {
                    // Find target by employee ID
                    var targetClient = clients.FirstOrDefault(c => c.EmployeeId == message.TargetEmployeeId);
                    if (targetClient != null)
                    {
                        // Send to target
                        targetClient.SendMessage(message);
                        // Echo back to sender
                        client.SendMessage(message);
                        Console.WriteLine($"[CHAT] {client.EmployeeId} -> {message.TargetEmployeeId}: {message.Content}");
                    }
                    else
                    {
                        var errorMsg = new ChatMessage
                        {
                            Type = MessageType.Notice,
                            Content = $"User '{message.TargetEmployeeId}' not found or offline.",
                            Timestamp = DateTime.Now
                        };
                        client.SendMessage(errorMsg);
                    }
                }
            }
            else
            {
                // No target specified, send error
                var errorMsg = new ChatMessage
                {
                    Type = MessageType.Notice,
                    Content = "Please select a recipient.",
                    Timestamp = DateTime.Now
                };
                client.SendMessage(errorMsg);
            }
        }

        /// <summary>
        /// Whisper message handler
        /// </summary>
        private void HandleWhisper(ChatClient client, ChatMessage message)
        {
            message.NickName = client.NickName;
            message.IpAddress = client.IpAddress;
            message.Timestamp = DateTime.Now;

            lock (clientsLock)
            {
                var targetClient = clients.FirstOrDefault(c => c.NickName == message.TargetNickName);
                if (targetClient != null)
                {
                    targetClient.SendMessage(message);
                    Console.WriteLine($"[WHISPER] {client.NickName} -> {message.TargetNickName}: {message.Content}");
                }
                else
                {
                    var errorMsg = new ChatMessage
                    {
                        Type = MessageType.Notice,
                        Content = $"User '{message.TargetNickName}' not found.",
                        Timestamp = DateTime.Now
                    };
                    client.SendMessage(errorMsg);
                }
            }
        }

        /// <summary>
        /// 사용자 목록 요청 처리
        /// </summary>
        private void HandleUserListRequest(ChatClient client)
        {
            lock (clientsLock)
            {
                // Format: "employeeId1:nickName1:userGroup1,employeeId2:nickName2:userGroup2,..."
                // Filter: dev can see all, others can only see same group
                var filteredClients = clients
                    .Where(c => !string.IsNullOrEmpty(c.NickName) && !string.IsNullOrEmpty(c.EmployeeId))
                    .Where(c => client.UserGroup == "dev" || c.UserGroup == client.UserGroup);

                var userListStr = string.Join(",", filteredClients
                    .Select(c => $"{c.EmployeeId}:{c.NickName}:{c.UserGroup ?? "default"}"));

                var response = new ChatMessage
                {
                    Type = MessageType.UserListResponse,
                    Content = userListStr,
                    Timestamp = DateTime.Now
                };

                client.SendMessage(response);

                Console.WriteLine($"[USER_LIST] Sent to {client.EmployeeId} ({client.UserGroup}): {userListStr}");
            }
        }

        /// <summary>
        /// Ping 처리
        /// </summary>
        private void HandlePing(ChatClient client)
        {
            var pongMsg = new ChatMessage
            {
                Type = MessageType.Pong,
                Timestamp = DateTime.Now
            };
            client.SendMessage(pongMsg);
        }

        /// <summary>
        /// 클라이언트 연결 해제 이벤트 핸들러
        /// </summary>
        private void OnClientDisconnected(object sender, string clientId)
        {
            ChatClient disconnectedClient = null;

            lock (clientsLock)
            {
                disconnectedClient = clients.FirstOrDefault(c => c.ClientId == clientId);
                if (disconnectedClient != null)
                {
                    clients.Remove(disconnectedClient);
                }
            }

            if (disconnectedClient != null)
            {
                Console.WriteLine($"[DISCONNECTED] {disconnectedClient.NickName ?? disconnectedClient.IpAddress} (ID: {clientId})");

                if (!string.IsNullOrEmpty(disconnectedClient.NickName))
                {
                    var noticeMsg = new ChatMessage
                    {
                        Type = MessageType.Notice,
                        Content = $"{disconnectedClient.NickName} has left the chat.",
                        Timestamp = DateTime.Now
                    };
                    BroadcastMessage(noticeMsg);
                }
            }
        }

        /// <summary>
        /// 모든 클라이언트에게 메시지 전송
        /// </summary>
        private void BroadcastMessage(ChatMessage message, string excludeClientId = null)
        {
            lock (clientsLock)
            {
                foreach (var client in clients.ToList())
                {
                    if (excludeClientId != null && client.ClientId == excludeClientId)
                        continue;

                    if (!string.IsNullOrEmpty(client.NickName))
                    {
                        client.SendMessage(message);
                    }
                }
            }
        }

        /// <summary>
        /// 서버 상태 출력
        /// </summary>
        public void PrintStatus()
        {
            Console.WriteLine("\n========================================");
            Console.WriteLine($"Current Clients: {clients.Count}/{maxClients}");
            Console.WriteLine("========================================");

            lock (clientsLock)
            {
                foreach (var client in clients)
                {
                    if (!string.IsNullOrEmpty(client.NickName))
                    {
                        Console.WriteLine($"- {client.NickName} ({client.EmployeeId}, {client.UserGroup})");
                        Console.WriteLine($"  IP: {client.IpAddress}, Host: {client.HostName}");
                        Console.WriteLine($"  Connected: {client.ConnectedTime:yyyy-MM-dd HH:mm:ss}");
                        Console.WriteLine($"  Last Activity: {client.LastActivity:HH:mm:ss}");
                    }
                }
            }

            Console.WriteLine("========================================\n");
        }

        /// <summary>
        /// TCP 서버 중지
        /// </summary>
        public void Stop()
        {
            if (!isRunning)
                return;

            isRunning = false;

            // Disconnect all clients
            lock (clientsLock)
            {
                foreach (var client in clients.ToList())
                {
                    client.Disconnect();
                }
                clients.Clear();
            }

            try
            {
                tcpListener?.Stop();
            }
            catch { }

            Console.WriteLine("[TCP] Chat server stopped");
        }
    }
}
