using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Globalization;

namespace Электронная_Фармация.Classes
{
    public sealed class InvoiceRequestService
    {
        public string CreateOrUpdateFromOrder(string orderId)
        {
            if (string.IsNullOrWhiteSpace(orderId))
            {
                throw new ArgumentException("Не указан заказ для формирования накладной.", nameof(orderId));
            }

            using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
            {
                con.Open();

                string orderNumber;
                string supplierName;
                string consigneeName;
                string orderState;

                using (var cmd = new SQLiteCommand(@"
select
    o.[OrderNumber],
    o.[SupplierName],
    ifnull(o.[ConsigneeName], '') as [ConsigneeName],
    ifnull(o.[OrderState], '') as [OrderState]
from [Orders] o
where o.[Id_Order] = @orderId;", con))
                {
                    cmd.Parameters.AddWithValue("@orderId", orderId);
                    using (var reader = cmd.ExecuteReader())
                    {
                        if (!reader.Read())
                        {
                            throw new InvalidOperationException("Выбранный заказ не найден.");
                        }

                        orderNumber = reader["OrderNumber"]?.ToString() ?? string.Empty;
                        supplierName = reader["SupplierName"]?.ToString() ?? string.Empty;
                        consigneeName = reader["ConsigneeName"]?.ToString() ?? string.Empty;
                        orderState = reader["OrderState"]?.ToString() ?? string.Empty;
                    }
                }

                if (!string.Equals(orderState, "ОТПРАВЛЕН", StringComparison.OrdinalIgnoreCase))
                {
                    throw new InvalidOperationException("Получить накладную можно только для заказа со статусом ОТПРАВЛЕН.");
                }

                var items = new List<InvoiceOrderItem>();
                using (var cmd = new SQLiteCommand(@"
select
    ifnull([es_code], 0) as [GoodCode],
    [DrugName],
    [Price],
    ifnull([Zakaz], 0) as [Zakaz],
    ifnull([RefuseItemsCount], 0) as [RefuseItemsCount],
    ifnull([ExpiryPeriod], '') as [ExpiryPeriod]
from [OrderItems]
where [Id_Order] = @orderId
order by [DrugName];", con))
                {
                    cmd.Parameters.AddWithValue("@orderId", orderId);
                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            int orderedQty = SafeInt(reader["Zakaz"]);
                            int refusedQty = SafeInt(reader["RefuseItemsCount"]);
                            int arrivedQty = Math.Max(orderedQty - refusedQty, 0);
                            if (arrivedQty <= 0)
                            {
                                continue;
                            }

                            var price = SafeDecimal(reader["Price"]);
                            items.Add(new InvoiceOrderItem
                            {
                                GoodCode = SafeInt(reader["GoodCode"]),
                                GoodName = reader["DrugName"]?.ToString() ?? string.Empty,
                                Price = price,
                                ArrivedQty = arrivedQty,
                                ExpiryPeriod = reader["ExpiryPeriod"]?.ToString() ?? string.Empty
                            });
                        }
                    }
                }

                if (items.Count == 0)
                {
                    throw new InvalidOperationException("Для этого заказа нет фактически пришедших позиций.");
                }

                int invoiceId;
                string invoiceNumber;
                using (var findCmd = new SQLiteCommand(@"
select [idInvoice], [InvoiceNumber]
from [Invoice]
where [SourceOrderId] = @orderId
limit 1;", con))
                {
                    findCmd.Parameters.AddWithValue("@orderId", orderId);
                    using (var reader = findCmd.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            invoiceId = Convert.ToInt32(reader["idInvoice"]);
                            invoiceNumber = reader["InvoiceNumber"]?.ToString() ?? string.Empty;
                        }
                        else
                        {
                            invoiceId = 0;
                            invoiceNumber = $"НКЛ-{orderNumber}";
                        }
                    }
                }

                var invoiceDate = DateTime.Now.ToString("yyyy-MM-dd");
                var invoiceSum = 0m;
                foreach (var item in items)
                {
                    invoiceSum += item.Price * item.ArrivedQty;
                }

                if (invoiceId == 0)
                {
                    using (var insertCmd = new SQLiteCommand(@"
insert into [Invoice]
(
    [InvoiceNumber],
    [InvoiceDate],
    [CodeSupplier],
    [CodeConsignees],
    [SumWithoutNDS],
    [SumWithNDS],
    [SumNDS],
    [SupplierName],
    [ConsigneesName],
    [InvoiceSum],
    [RefuseSum],
    [SourceOrderId],
    [SourceOrderNumber],
    [IsRequestedInvoice]
)
values
(
    @number,
    @date,
    0,
    0,
    @sum,
    @sum,
    0,
    @supplier,
    @consignee,
    @invoiceSum,
    0,
    @sourceOrderId,
    @sourceOrderNumber,
    1
);
select last_insert_rowid();", con))
                    {
                        insertCmd.Parameters.AddWithValue("@number", invoiceNumber);
                        insertCmd.Parameters.AddWithValue("@date", invoiceDate);
                        insertCmd.Parameters.AddWithValue("@sum", invoiceSum);
                        insertCmd.Parameters.AddWithValue("@supplier", supplierName);
                        insertCmd.Parameters.AddWithValue("@consignee", consigneeName);
                        insertCmd.Parameters.AddWithValue("@invoiceSum", invoiceSum);
                        insertCmd.Parameters.AddWithValue("@sourceOrderId", orderId);
                        insertCmd.Parameters.AddWithValue("@sourceOrderNumber", orderNumber);
                        invoiceId = Convert.ToInt32(insertCmd.ExecuteScalar());
                    }
                }
                else
                {
                    using (var updateCmd = new SQLiteCommand(@"
update [Invoice]
set
    [InvoiceDate] = @date,
    [SumWithoutNDS] = @sum,
    [SumWithNDS] = @sum,
    [SumNDS] = 0,
    [SupplierName] = @supplier,
    [ConsigneesName] = @consignee,
    [InvoiceSum] = @invoiceSum,
    [RefuseSum] = 0,
    [SourceOrderNumber] = @sourceOrderNumber,
    [IsRequestedInvoice] = 1
where [idInvoice] = @invoiceId;", con))
                    {
                        updateCmd.Parameters.AddWithValue("@date", invoiceDate);
                        updateCmd.Parameters.AddWithValue("@sum", invoiceSum);
                        updateCmd.Parameters.AddWithValue("@supplier", supplierName);
                        updateCmd.Parameters.AddWithValue("@consignee", consigneeName);
                        updateCmd.Parameters.AddWithValue("@invoiceSum", invoiceSum);
                        updateCmd.Parameters.AddWithValue("@sourceOrderNumber", orderNumber);
                        updateCmd.Parameters.AddWithValue("@invoiceId", invoiceId);
                        updateCmd.ExecuteNonQuery();
                    }

                    using (var deleteItemsCmd = new SQLiteCommand("delete from [InvoiceItem] where [idInvoice] = @invoiceId;", con))
                    {
                        deleteItemsCmd.Parameters.AddWithValue("@invoiceId", invoiceId);
                        deleteItemsCmd.ExecuteNonQuery();
                    }
                }

                foreach (var item in items)
                {
                    decimal lineSum = item.Price * item.ArrivedQty;
                    using (var itemCmd = new SQLiteCommand(@"
insert into [InvoiceItem]
(
    [idGood],
    [GoodCode],
    [Good],
    [ProducerName],
    [CountryName],
    [idInvoice],
    [GoodsGount],
    [PriceSupplierWithoutNDS],
    [PriceSupplierWithNDS],
    [PriceProducerWithoutNDS],
    [PriceProducerWithNDS],
    [NDS],
    [JNVLS],
    [PriceReestr],
    [SumWithoutNDS],
    [SumWithNDS],
    [SumNDS],
    [Serial],
    [BestBefore],
    [Sertificate],
    [Marked],
    [GTIN]
)
values
(
    null,
    @goodCode,
    @good,
    '',
    '',
    @invoiceId,
    @qty,
    @price,
    @price,
    null,
    null,
    0,
    0,
    null,
    @sum,
    @sum,
    0,
    '',
    @bestBefore,
    '',
    0,
    ''
);", con))
                    {
                        itemCmd.Parameters.AddWithValue("@goodCode", item.GoodCode);
                        itemCmd.Parameters.AddWithValue("@good", item.GoodName);
                        itemCmd.Parameters.AddWithValue("@invoiceId", invoiceId);
                        itemCmd.Parameters.AddWithValue("@qty", item.ArrivedQty);
                        itemCmd.Parameters.AddWithValue("@price", item.Price);
                        itemCmd.Parameters.AddWithValue("@sum", lineSum);
                        itemCmd.Parameters.AddWithValue("@bestBefore", item.ExpiryPeriod);
                        itemCmd.ExecuteNonQuery();
                    }
                }

                return orderId;
            }
        }

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

            int result;
            return int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result)
                || int.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture, out result)
                ? result
                : 0;
        }

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

            var text = value.ToString()?.Trim();
            if (string.IsNullOrWhiteSpace(text))
            {
                return 0m;
            }

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

            text = text.Replace(',', '.');
            if (decimal.TryParse(text, NumberStyles.Number, CultureInfo.InvariantCulture, out result))
            {
                return result;
            }

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

            return 0m;
        }

        private sealed class InvoiceOrderItem
        {
            public int GoodCode { get; set; }
            public string GoodName { get; set; }
            public decimal Price { get; set; }
            public int ArrivedQty { get; set; }
            public string ExpiryPeriod { get; set; }
        }
    }
}
