using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Windows.Forms;
using Электронная_Фармация.HelpForms;
using Электронная_Фармация.Properties;

namespace Электронная_Фармация.Classes
{
    public class DataSender
    {
        private const string BuyerOrdersEndpoint = "/api/buyer/orders";

        private string _baseUrl;
        private string _token;

        public async Task Main()
        {
            _token = Settings.Default.stringToken;
            _baseUrl = AppConfig.ApiBaseUrl;

            if (string.IsNullOrWhiteSpace(_token))
            {
                AppDebugLog.Error("DataSender", "Отправка заказов: токен отсутствует");
                ToastNotification.ShowError("Не найден токен авторизации.");
                return;
            }

            List<BuyerOrderRequest> orders;
            try
            {
                orders = FetchDataFromSql(null);
            }
            catch (Exception ex)
            {
                AppDebugLog.Error("DataSender", "Ошибка подготовки заказов к отправке", ex);
                ToastNotification.ShowError($"Ошибка подготовки заказов: {ex.Message}");
                return;
            }
            if (orders.Count == 0)
            {
                ToastNotification.ShowCustom("Нет новых заказов для отправки.", Color.DarkOrange, Color.White);
                return;
            }

            if (!EnsureOrdersHaveLocationId(orders))
            {
                return;
            }

            int successCount = 0;
            int failCount = 0;

            foreach (var order in orders)
            {
                bool ok = await SendOrderAsync(order);
                if (ok)
                {
                    successCount++;
                }
                else
                {
                    failCount++;
                }
            }

            if (successCount > 0 && failCount == 0)
            {
                ToastNotification.ShowSuccess($"Отправлено заказов: {successCount}");
            }
            else if (successCount > 0)
            {
                ToastNotification.ShowCustom(
                    $"Отправлено: {successCount}, с ошибкой: {failCount}",
                    Color.DarkOrange,
                    Color.White);
            }
        }

        /// <summary>
        /// Sends a single local order by Id_Order (must be in state НОВЫЙ).
        /// </summary>
        public async Task<bool> SendSingleOrderAsync(string localOrderId)
        {
            _token = Settings.Default.stringToken;
            _baseUrl = AppConfig.ApiBaseUrl;

            if (string.IsNullOrWhiteSpace(_token))
            {
                AppDebugLog.Error("DataSender", $"Отправка заказа {localOrderId}: токен отсутствует");
                ToastNotification.ShowError("Не найден токен авторизации.");
                return false;
            }

            List<BuyerOrderRequest> orders;
            try
            {
                orders = FetchDataFromSql(localOrderId);
            }
            catch (Exception ex)
            {
                AppDebugLog.Error("DataSender", $"Ошибка подготовки заказа {localOrderId}", ex);
                ToastNotification.ShowError($"Ошибка подготовки заказа: {ex.Message}");
                return false;
            }
            if (orders.Count == 0)
            {
                ToastNotification.ShowCustom("Заказ не найден или уже отправлен.", Color.DarkOrange, Color.White);
                return false;
            }

            if (!EnsureOrdersHaveLocationId(orders))
            {
                return false;
            }

            bool ok = await SendOrderAsync(orders[0]);
            if (ok)
            {
                ToastNotification.ShowSuccess("Заказ отправлен");
            }

            return ok;
        }

        public List<BuyerOrderRequest> FetchDataFromSql(string onlyOrderId)
        {
            var orders = new Dictionary<string, BuyerOrderRequest>();
            var locationCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
            {
                connection.Open();
                string query = @"
SELECT
    Orders.Id_Order,
    Orders.OrderNumber,
    Orders.ConsigneeName,
    OrderItems.supplier_price_id,
    OrderItems.DrugName,
    OrderItems.es_code,
    OrderItems.Zakaz,
    Orders.Comment
FROM Orders
INNER JOIN OrderItems ON OrderItems.Id_Order = Orders.Id_Order
WHERE Orders.OrderState = 'НОВЫЙ'
";
                if (!string.IsNullOrWhiteSpace(onlyOrderId))
                {
                    query += " AND Orders.Id_Order = @orderId";
                }

                query += " ORDER BY Orders.Id_Order";

                using (var command = new SQLiteCommand(query, connection))
                {
                    if (!string.IsNullOrWhiteSpace(onlyOrderId))
                    {
                        command.Parameters.AddWithValue("@orderId", onlyOrderId);
                    }

                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            string orderId = reader["Id_Order"].ToString().Trim();
                            string orderNumber = reader["OrderNumber"] == DBNull.Value
                                ? string.Empty
                                : reader["OrderNumber"].ToString().Trim();
                            string consigneeName = reader["ConsigneeName"] == DBNull.Value
                                ? string.Empty
                                : reader["ConsigneeName"].ToString().Trim();
                            string supplierPriceId = reader["supplier_price_id"].ToString().Trim();
                            string drugName = reader["DrugName"] == DBNull.Value ? null : reader["DrugName"].ToString().Trim();
                            string esCode = reader["es_code"] == DBNull.Value ? null : reader["es_code"].ToString().Trim();
                            decimal qty = ReadDecimal(reader["Zakaz"]);

                            if (string.IsNullOrWhiteSpace(supplierPriceId))
                            {
                                throw new InvalidOperationException($"В заказе {orderId} есть позиция без supplier_price_id.");
                            }

                            if (qty <= 0)
                            {
                                throw new InvalidOperationException($"В заказе {orderId} количество должно быть больше нуля.");
                            }

                            if (!orders.TryGetValue(orderId, out BuyerOrderRequest order))
                            {
                                string locationId = ResolveLocationIdCached(consigneeName, locationCache);
                                order = new BuyerOrderRequest
                                {
                                    LocalOrderId = orderId,
                                    GlobalSign = OrderNumberHelper.IsValidEx(orderNumber)
                                        ? OrderNumberHelper.Normalize(orderNumber)
                                        : null,
                                    LocationId = locationId,
                                    Comment = reader["Comment"] == DBNull.Value ? null : reader["Comment"].ToString()
                                };
                                orders.Add(orderId, order);
                            }

                            order.Items.Add(new BuyerOrderItem
                            {
                                SupplierPriceId = supplierPriceId,
                                Qty = qty,
                                ItemName = string.IsNullOrWhiteSpace(drugName) ? null : drugName,
                                ItemCode = string.IsNullOrWhiteSpace(esCode) ? null : esCode
                            });
                        }
                    }
                }
            }

            return orders.Values.ToList();
        }

        private static string ResolveLocationIdCached(string consigneeName, Dictionary<string, string> cache)
        {
            var key = consigneeName ?? string.Empty;
            if (cache.TryGetValue(key, out var cached))
            {
                return cached;
            }

            var locationId = ConsigneeHelper.ResolveLocationId(consigneeName);
            cache[key] = locationId ?? string.Empty;
            return cache[key];
        }

        private bool EnsureOrdersHaveLocationId(List<BuyerOrderRequest> orders)
        {
            var missing = orders
                .Where(o => string.IsNullOrWhiteSpace(o.LocationId))
                .Select(o => o.LocalOrderId)
                .Distinct()
                .ToList();

            if (missing.Count == 0)
            {
                return true;
            }

            if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
            {
                if (!HF_LocationId.PromptAndSave(Form.ActiveForm)
                    || string.IsNullOrWhiteSpace(AppConfig.LocationId))
                {
                    AppDebugLog.Error("DataSender", "Отправка заказов: location_id не указан");
                    ToastNotification.ShowCustom(
                        "Не указан Location ID у аптеки заказа. Задайте его в «Грузополучатели» или в настройках.",
                        Color.DarkOrange,
                        Color.White);
                    return false;
                }

                ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
            }

            foreach (var order in orders.Where(o => string.IsNullOrWhiteSpace(o.LocationId)))
            {
                order.LocationId = AppConfig.LocationId;
            }

            if (orders.Any(o => string.IsNullOrWhiteSpace(o.LocationId)))
            {
                ToastNotification.ShowCustom(
                    "У части заказов нет Location ID. Укажите ID у грузополучателя.",
                    Color.DarkOrange,
                    Color.White);
                return false;
            }

            return true;
        }

        private async Task<bool> SendOrderAsync(BuyerOrderRequest order)
        {
            var options = new JsonSerializerOptions
            {
                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
            };
            string json = JsonSerializer.Serialize(order, options);
            var fullUrl = new Uri(new Uri(_baseUrl.TrimEnd('/') + "/"), BuyerOrdersEndpoint.TrimStart('/')).ToString();
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();

            AppDebugLog.ApiRequest("POST", fullUrl, json, $"order={order.LocalOrderId}");

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(_baseUrl);
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);

                var content = new StringContent(json, Encoding.UTF8, "application/json");
                try
                {
                    HttpResponseMessage response = await client.PostAsync(BuyerOrdersEndpoint, content);
                    string responseString = await response.Content.ReadAsStringAsync();
                    stopwatch.Stop();

                    AppDebugLog.ApiResponse("POST", fullUrl, (int)response.StatusCode, stopwatch.ElapsedMilliseconds, responseString);

                    if (!response.IsSuccessStatusCode)
                    {
                        AppDebugLog.ApiHttpError(
                            "POST",
                            fullUrl,
                            (int)response.StatusCode,
                            responseString,
                            $"order={order.LocalOrderId}");
                        AppDebugLog.Error(
                            "DataSender",
                            $"Ошибка отправки заказа {order.LocalOrderId}: HTTP {(int)response.StatusCode} {response.ReasonPhrase}");
                        ToastNotification.ShowError(
                            $"Ошибка отправки заказа {order.LocalOrderId}: {(int)response.StatusCode} {response.ReasonPhrase}\n{responseString}");
                        return false;
                    }

                    var createdOrder = JsonSerializer.Deserialize<BuyerOrderCreateResult>(responseString, options);
                    if (createdOrder == null || string.IsNullOrWhiteSpace(createdOrder.OrderID))
                    {
                        throw new InvalidOperationException("Сервер не вернул order_id созданного заказа.");
                    }

                    // Старые серверы создавали Draft — дожимаем до Placed.
                    var remoteStatus = createdOrder.Status;
                    if (!string.Equals(remoteStatus, "Placed", StringComparison.OrdinalIgnoreCase))
                    {
                        remoteStatus = await PlaceOrderAsync(client, createdOrder.OrderID) ?? remoteStatus;
                    }

                    MarkOrderSent(order.LocalOrderId, createdOrder.OrderID, remoteStatus, createdOrder.GlobalSign);
                    AppDebugLog.Info("DataSender",
                        $"Заказ {order.LocalOrderId} отправлен, remote={createdOrder.OrderID}, status={remoteStatus}, sign={createdOrder.GlobalSign}");
                    return true;
                }
                catch (InvalidOperationException ex)
                {
                    stopwatch.Stop();
                    AppDebugLog.Error("DataSender", $"Ошибка отправки заказа {order.LocalOrderId}", ex);
                    ToastNotification.ShowError(ex.Message);
                    return false;
                }
                catch (Exception ex)
                {
                    stopwatch.Stop();
                    AppDebugLog.ApiFailure("POST", fullUrl, stopwatch.ElapsedMilliseconds, ex);
                    ToastNotification.ShowError($"Ошибка сети при отправке заказа {order.LocalOrderId}: {ex.Message}");
                    return false;
                }
            }
        }

        private async Task<string> PlaceOrderAsync(HttpClient client, string remoteOrderId)
        {
            var placePath = $"{BuyerOrdersEndpoint}/{Uri.EscapeDataString(remoteOrderId)}/place";
            var fullUrl = new Uri(new Uri(_baseUrl.TrimEnd('/') + "/"), placePath.TrimStart('/')).ToString();
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            AppDebugLog.ApiRequest("POST", fullUrl, string.Empty, $"place={remoteOrderId}");

            try
            {
                using (var content = new StringContent(string.Empty, Encoding.UTF8, "application/json"))
                using (HttpResponseMessage response = await client.PostAsync(placePath, content))
                {
                    string responseString = await response.Content.ReadAsStringAsync();
                    stopwatch.Stop();
                    AppDebugLog.ApiResponse("POST", fullUrl, (int)response.StatusCode, stopwatch.ElapsedMilliseconds, responseString);

                    if (!response.IsSuccessStatusCode)
                    {
                        AppDebugLog.ApiHttpError(
                            "POST",
                            fullUrl,
                            (int)response.StatusCode,
                            responseString,
                            $"place={remoteOrderId}");
                        throw new InvalidOperationException(
                            $"Не удалось оформить заказ на сервере (HTTP {(int)response.StatusCode}). " +
                            "Заказ остался черновиком (Draft).");
                    }

                    try
                    {
                        using (var doc = JsonDocument.Parse(responseString))
                        {
                            if (doc.RootElement.TryGetProperty("status", out var statusEl))
                            {
                                return statusEl.GetString();
                            }
                        }
                    }
                    catch
                    {
                        // ignore parse — place succeeded
                    }

                    return "Placed";
                }
            }
            catch (InvalidOperationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                stopwatch.Stop();
                AppDebugLog.ApiFailure("POST", fullUrl, stopwatch.ElapsedMilliseconds, ex);
                throw new InvalidOperationException(
                    $"Не удалось оформить заказ на сервере: {ex.Message}", ex);
            }
        }

        private static void MarkOrderSent(string localOrderId, string remoteOrderId, string remoteStatus, string globalSign)
        {
            if (string.IsNullOrWhiteSpace(localOrderId))
            {
                return;
            }

            using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
            {
                con.Open();
                using (var cmd = new SQLiteCommand(
                    @"UPDATE [Orders]
                      SET [OrderState] = @state,
                          [RemoteOrderId] = @remoteOrderId,
                          [OrderNumber] = CASE
                              WHEN @globalSign IS NOT NULL AND length(trim(@globalSign)) > 0 THEN @globalSign
                              ELSE [OrderNumber]
                          END
                      WHERE [Id_Order] = @id AND [OrderState] = 'НОВЫЙ'",
                    con))
                {
                    cmd.Parameters.AddWithValue("@state", "ОТПРАВЛЕН");
                    cmd.Parameters.AddWithValue("@remoteOrderId", remoteOrderId ?? string.Empty);
                    cmd.Parameters.AddWithValue("@globalSign",
                        string.IsNullOrWhiteSpace(globalSign) ? (object)DBNull.Value : globalSign.Trim());
                    cmd.Parameters.AddWithValue("@id", localOrderId);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        private static decimal ReadDecimal(object value)
        {
            if (value == null || value == DBNull.Value)
            {
                return 0;
            }

            string text = value.ToString();
            if (decimal.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out decimal invariantValue))
            {
                return invariantValue;
            }

            if (decimal.TryParse(text, NumberStyles.Number, CultureInfo.CurrentCulture, out decimal currentCultureValue))
            {
                return currentCultureValue;
            }

            throw new InvalidOperationException($"Некорректное количество: {text}");
        }
    }

    public class BuyerOrderRequest
    {
        [JsonIgnore]
        public string LocalOrderId { get; set; }

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

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

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

        [JsonPropertyName("items")]
        public List<BuyerOrderItem> Items { get; set; } = new List<BuyerOrderItem>();
    }

    public class BuyerOrderItem
    {
        [JsonPropertyName("supplier_price_id")]
        public string SupplierPriceId { get; set; }

        [JsonPropertyName("qty")]
        public decimal Qty { get; set; }

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

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

    public sealed class BuyerOrderCreateResult
    {
        [JsonPropertyName("order_id")]
        public string OrderID { get; set; }

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

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