using CheckEVS;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DahuaDiskChecker
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string server = string.Empty;

            //Console.WriteLine("=== ПРОВЕРКА СОСТОЯНИЯ ДИСКА DAHUA ===");
#if DEBUG
            Start:
            //Console.WriteLine("Введи ip-адрес EVS:");
            server = "172.16.48.144";
            //Console.WriteLine("Введи номер диска: (0 - количество неисправных, 1-24 - статус указанного");
            int diskNumber = 6;
#else

            // Парсинг аргументов
            if (args.Length < 2)
            {
                await PromoPrint();
                
                //Console.WriteLine("Использование: DahuaDiskChecker.exe <IP> <номер_диска> [пользователь] [пароль]");
                //Console.WriteLine("Пример: DahuaDiskChecker.exe 172.16.48.147 10 admin admin");
                return;
            }
            server = args[0];
            if (!int.TryParse(args[1], out int diskNumber))
            {
                //Console.WriteLine("❌ Ошибка: номер диска должен быть числом");
                return;
            }
#endif
            string user = args.Length > 2 ? args[2] : "admin";
            string password = args.Length > 3 ? args[3] : "4NUDZhJ7";

            try
            {
                var checker = new DahuaDiskChecker();
                string status = await checker.CheckDiskStatusAsync(server, diskNumber, user, password);

                // Вывод результата
                //Console.WriteLine();
                //Console.WriteLine(new string('=', 40));

                try
                {
                    Convert.ToInt32(status);
                    Console.WriteLine(status);
                }
                catch (Exception)
                {
                    if (status == "NotFound")
                    {
                        Console.WriteLine("10");
                    }
                    else if (status == "Good")
                    {
                        Console.WriteLine("0");
                    }
                    else
                    {
                        Console.WriteLine("1");
                    }
                }
#if DEBUG
                Console.ReadKey();
                Console.WriteLine("=======================================");
                goto Start;
#endif

                //Console.WriteLine(new string('=', 40));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ Ошибка: {ex.Message}");
            }

        }

        private static async Task PromoPrint()
        {
            //  ╔  ═  ╦  ╗  ║  ╟  ╤ │ ─ ┷   ╢  ╚  ╝
            Console.WriteLine("╔════════════════════════════╤════════════╗");
            Console.WriteLine("║ autor: mastankov           │  ver: 0.1  ║");
            Console.WriteLine("╟────────────────────────────┴────────────╢");
            Console.WriteLine("║ Пример:                                 ║");
            Console.WriteLine("║ DahuaDiskChecker.exe <IP> <номер_диска> ║");
            Console.WriteLine("║ Вернет статус запрошенного диска        ║");
            Console.WriteLine("║                                         ║");
            Console.WriteLine("║ DahuaDiskChecker.exe <IP> 0             ║");
            Console.WriteLine("║ Вернет количество неисправных дисков    ║");
            Console.WriteLine("║                                         ║");
            Console.WriteLine("║ Нажми Enter, чтобы выйти                ║");
            Console.WriteLine("╚═════════════════════════════════════════╝");
            //Console.ReadKey();
        }
    }

    public class DahuaDiskChecker
    {
        private readonly HttpClient _httpClient;

        public DahuaDiskChecker()
        {
            _httpClient = new HttpClient();
            _httpClient.Timeout = TimeSpan.FromSeconds(30);
        }

        // Вычисление MD5 хэша
        private string CalculateMD5(string input)
        {
            using (MD5 md5 = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                StringBuilder sb = new StringBuilder();
                foreach (byte b in hashBytes)
                {
                    sb.Append(b.ToString("X2"));
                }
                return sb.ToString();
            }
        }

        // Получение сессии
        private async Task<string> GetSessionAsync(string server, string user, string password)
        {
            // 1. Challenge запрос
            var challengeRequest = new
            {
                method = "global.login",
                @params = new
                {
                    userName = user,
                    password = "",
                    clientType = "Web3.0"
                },
                id = 1,
                session = (string)null
            };

            string json = JsonSerializer.Serialize(challengeRequest);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync($"http://{server}/RPC2_Login", content);
            response.EnsureSuccessStatusCode();

            var responseJson = await response.Content.ReadAsStringAsync();
            using JsonDocument doc = JsonDocument.Parse(responseJson);
            var root = doc.RootElement;

            if (!root.TryGetProperty("result", out var resultProp) || resultProp.GetBoolean())
                throw new Exception("Не удалось получить challenge");

            string realm = root.GetProperty("params").GetProperty("realm").GetString();
            string random = root.GetProperty("params").GetProperty("random").GetString();
            string session = root.GetProperty("session").GetString();

            // 2. Вычисление пароля
            string pwdHash = CalculateMD5($"{user}:{realm}:{password}");
            string passHash = CalculateMD5($"{user}:{random}:{pwdHash}");

            // 3. Авторизация
            var authRequest = new
            {
                method = "global.login",
                @params = new
                {
                    userName = user,
                    clientType = "Web3.0",
                    authorityType = "Default",
                    passwordType = "Default",
                    password = passHash
                },
                id = 2,
                session = session
            };

            json = JsonSerializer.Serialize(authRequest);
            content = new StringContent(json, Encoding.UTF8, "application/json");

            response = await _httpClient.PostAsync($"http://{server}/RPC2_Login", content);
            response.EnsureSuccessStatusCode();

            responseJson = await response.Content.ReadAsStringAsync();
            using JsonDocument authDoc = JsonDocument.Parse(responseJson);
            var authRoot = authDoc.RootElement;

            if (!authRoot.TryGetProperty("result", out resultProp) || !resultProp.GetBoolean())
                throw new Exception("Ошибка авторизации");

            return authRoot.GetProperty("session").GetString();
        }

        // Получение состояния диска
        public async Task<string> CheckDiskStatusAsync(string server, int diskNumber, string user, string password)
        {
            try
            {
                // Получаем сессию
                string session = await GetSessionAsync(server, user, password);

                // Запрос информации о дисках
                var diskRequest = new
                {
                    method = "storage.getTankInfo",
                    @params = (object)null,
                    id = 270,
                    session
                };

                string json = JsonSerializer.Serialize(diskRequest);
                var content = new StringContent(json, Encoding.UTF8, "application/json");

                var response = await _httpClient.PostAsync($"http://{server}/RPC2", content);
                response.EnsureSuccessStatusCode();

                var responseJson = await response.Content.ReadAsStringAsync();
                var listDisk = Newtonsoft.Json.JsonConvert.DeserializeObject<Disks>(responseJson).@params.tank[0].Device;
                if (diskNumber != 0)
                {
                    // Парсим ответ для поиска диска
                    string diskStatus = ParseDiskInfo(responseJson, diskNumber);
                    return diskStatus ?? "NotFound";
                }
                else
                {
                    int countDiskWithError = 0;
                    for (int i = 0; i < listDisk.Count - 2; i++)
                    {
                        if (listDisk[i].PreDiskCheck != "Good" && listDisk[i].PreDiskCheck != "Warn")
                            countDiskWithError++;
                    }
                    return countDiskWithError.ToString();
                }
            }
            catch (HttpRequestException ex)
            {
                throw new Exception($"Ошибка сети: {ex.Message}");
            }
            catch (Exception ex)
            {
                throw new Exception($"Ошибка: {ex.Message}");
            }
        }

        // Парсинг информации о дисках
        private string ParseDiskInfo(string jsonResponse, int diskNumber)
        {
            using JsonDocument doc = JsonDocument.Parse(jsonResponse);
            var root = doc.RootElement;

            // Проверяем успешность запроса
            if (!root.TryGetProperty("result", out var resultProp) || !resultProp.GetBoolean())
                return null;

            // Ищем диски
            if (!root.TryGetProperty("params", out var paramsProp))
                return null;

            if (!paramsProp.TryGetProperty("tank", out var tanksProp))
                return null;

            // Перебираем все массивы танков (хранилищ)
            foreach (var tank in tanksProp.EnumerateArray())
            {
                if (!tank.TryGetProperty("Device", out var devicesProp))
                    continue;

                // Перебираем устройства в танке
                foreach (var device in devicesProp.EnumerateArray())
                {
                    // Если устройство - строка (как в вашем примере)
                    if (device.ValueKind == JsonValueKind.String)
                    {
                        string deviceStr = device.GetString();
                        if (deviceStr.Contains($"PhysicNo={diskNumber}"))
                        {
                            // Ищем PreDiskCheck
                            int start = deviceStr.IndexOf("PreDiskCheck=");
                            if (start == -1) return "Unknown";

                            start += "PreDiskCheck=".Length;
                            int end = deviceStr.IndexOf(';', start);
                            if (end == -1) end = deviceStr.Length;

                            return deviceStr.Substring(start, end - start).Trim();
                        }
                    }
                    // Если устройство - объект
                    else if (device.ValueKind == JsonValueKind.Object)
                    {
                        // Пробуем получить PhysicNo как строку или число
                        string physicNo = null;
                        if (device.TryGetProperty("PhysicNo", out var physicNoProp))
                        {
                            if (physicNoProp.ValueKind == JsonValueKind.String)
                            {
                                physicNo = physicNoProp.GetString();
                            }
                            else if (physicNoProp.ValueKind == JsonValueKind.Number)
                            {
                                physicNo = physicNoProp.GetInt32().ToString();
                            }
                        }

                        // Если нашли нужный диск
                        if (physicNo == diskNumber.ToString())
                        {
                            // Получаем статус
                            if (device.TryGetProperty("PreDiskCheck", out var statusProp))
                            {
                                return statusProp.GetString() ?? "Unknown";
                            }
                            return "Unknown";
                        }
                    }
                }
            }

            return null;
        }
    }


    // Классы для десериализации JSON

    public class DahuaResponse<T>
    {
        [JsonPropertyName("result")]
        public bool Result { get; set; }

        [JsonPropertyName("session")]
        public string Session { get; set; }

        [JsonPropertyName("params")]
        public T Params { get; set; }

        [JsonPropertyName("error")]
        public ErrorInfo Error { get; set; }

        [JsonPropertyName("id")]
        public int Id { get; set; }
    }

    public class ChallengeData
    {
        [JsonPropertyName("authorization")]
        public string Authorization { get; set; }

        [JsonPropertyName("random")]
        public string Random { get; set; }

        [JsonPropertyName("realm")]
        public string Realm { get; set; }

        [JsonPropertyName("opaque")]
        public string Opaque { get; set; }
    }

    public class AuthData
    {
        [JsonPropertyName("keepAliveInterval")]
        public int KeepAliveInterval { get; set; }

        [JsonPropertyName("isPwdOverdue")]
        public bool IsPwdOverdue { get; set; }
    }

    public class TankInfoData
    {
        [JsonPropertyName("tank")]
        public List<Tank> Tanks { get; set; }
    }

    public class Tank
    {
        [JsonPropertyName("Device")]
        public List<JsonElement> Devices { get; set; }

        [JsonPropertyName("FreeSpace")]
        public double FreeSpace { get; set; }

        [JsonPropertyName("Level")]
        public int Level { get; set; }

        [JsonPropertyName("SlotNum")]
        public int SlotNum { get; set; }

        [JsonPropertyName("TankNo")]
        public int TankNo { get; set; }

        [JsonPropertyName("Temperature")]
        public double Temperature { get; set; }

        [JsonPropertyName("TotalSpace")]
        public double TotalSpace { get; set; }
    }

    public class Device
    {
        [JsonPropertyName("PhysicNo")]
        public string PhysicNo { get; set; }

        [JsonPropertyName("PreDiskCheck")]
        public string PreDiskCheck { get; set; }

        [JsonPropertyName("Name")]
        public string Name { get; set; }

        [JsonPropertyName("SerialNo")]
        public string SerialNo { get; set; }

        [JsonPropertyName("Temperature")]
        public string Temperature { get; set; }

        [JsonPropertyName("State")]
        public string State { get; set; }
    }

    public class ErrorInfo
    {
        [JsonPropertyName("code")]
        public int Code { get; set; }

        [JsonPropertyName("message")]
        public string Message { get; set; }
    }
}