using AyCode.Core.Extensions;
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Toons;
using AyCode.Utils.Extensions;
using FruitBank.Common.Entities;
using FruitBank.Common.Enums;
using FruitBank.Common.Interfaces;
using FruitBank.Common.Models;
using LinqToDB.Mapping;
using Mango.Nop.Core.Dtos;
using Mango.Nop.Core.Extensions;
using Mango.Nop.Core.Interfaces;
using Newtonsoft.Json;
using Nop.Core;
//using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Orders;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq.Expressions;

namespace FruitBank.Common.Dtos;

[AcBinarySerializable(false, true, false, true, false, false)]
[LinqToDB.Mapping.Table(Name = nameof(Order))]
[System.ComponentModel.DataAnnotations.Schema.Table(nameof(Order))]
[ToonDescription($"Data transfer object for {nameof(Order)}", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(Order)])]
public class OrderDto : MgOrderDto<OrderItemDto, ProductDto>, IOrderDto
{
    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    private static Expression<Func<OrderDto, GenericAttributeDto, bool>> RelationWithGenericAttribute => (orderDto, genericAttributeDto) =>
        orderDto.Id == genericAttributeDto.EntityId && genericAttributeDto.KeyGroup == nameof(Order);


    [Association(ThisKey = nameof(Id), OtherKey = nameof(GenericAttribute.EntityId), ExpressionPredicate = nameof(RelationWithGenericAttribute), CanBeNull = true)]
    public List<GenericAttributeDto> GenericAttributes { get; set; }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => Id > 0 && OrderItemDtos.Count > 0 && OrderItemDtos.All(x => x.IsMeasured)", Constraints = "[#SmartTypeConstraints], readonly")]
    public bool IsMeasured
    {
        get => IsMeasuredAndValid();
        set => throw new Exception($"OrderDto.IsMeasured not set");
    }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => OrderItemDtos.Any(oi => oi.IsMeasurable)", Constraints = "[#SmartTypeConstraints], readonly")]
    public bool IsMeasurable
    {
        get => OrderItemDtos.Any(oi => oi.IsMeasurable);
        set => throw new Exception($"OrderDto.IsMeasurable not set");
    }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public string TimeOfReceiptText
    {
        get
        {
            var resultText = string.Empty;

            if (IsComplete) resultText = "[AUDITED]";
            else if (IsMeasured) resultText = "[KÉSZ]";
            else if (MeasurementOwnerId > 0) resultText = "[FOLYAMATBAN]";
            //else if (MeasurementOwnerId == ??) resultText = "[SAJÁT MÉRÉS]";
            //else if (MeasurementOwnerId > 0) resultText = "[MÁSOK MÉRIK]";

            return $"{resultText} {DateOfReceiptOrCreated:H:mm} - {Customer.Company}";
        }
    }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public DateTime DateOfReceiptOrCreated => DateOfReceipt ?? CreatedOnUtc;

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrNull<DateTime>('DateOfReceipt')")]
    public DateTime? DateOfReceipt => GenericAttributes.GetValueOrNull<DateTime>(nameof(IOrderDto.DateOfReceipt));

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => GenericAttributes single 'LicensePlate' value (raw, NAV mapper normalizes)")]
    public string? LicensePlate =>
        GenericAttributes.SingleOrDefault(x => x.Key == nameof(IOrderDto.LicensePlate))?.Value;

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => deserialize GenericAttributes single 'Site' JSON value to OrderSiteSnapshot")]
    public OrderSiteSnapshot? Site
    {
        get
        {
            var json = GenericAttributes.SingleOrDefault(x => x.Key == nameof(IOrderDto.Site))?.Value;
            return string.IsNullOrWhiteSpace(json) ? null : JsonConvert.DeserializeObject<OrderSiteSnapshot>(json);
        }
    }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('RevisorId', 0)")]
    public int RevisorId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.RevisorId), 0);

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => GenericAttributes.GetValueOrDefault('MeasurementOwnerId', 0)")]
    public int MeasurementOwnerId => GenericAttributes.GetValueOrDefault(nameof(IOrderDto.MeasurementOwnerId), 0);

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited)")]
    public bool IsAllOrderItemAudited => OrderItemDtos.Count > 0 && OrderItemDtos.All(oi => oi.IsAudited);

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => OrderItemDtos.All(oi => oi.AverageWeightIsValid)")]
    public bool IsAllOrderItemAvgWeightValid => OrderItemDtos.All(oi => oi.AverageWeightIsValid);

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public MeasuringStatus MeasuringStatus
    {
        get
        {
            if (IsComplete) return MeasuringStatus.Audited;
            if (IsMeasured) return MeasuringStatus.Finnished;

            return OrderItemDtos.Any(oip => oip.MeasuringStatus >= MeasuringStatus.Started) ? MeasuringStatus.Started : MeasuringStatus.NotStarted;
        }
    }
    //if (GenericAttributes.TryGetValue<DateTime>(nameof(IOrderDto.DateOfReceipt), out var value)) return value;
    //var dateOfReceipt = GenericAttributes.SingleOrDefault(x => x.Key == nameof(IOrderDto.DateOfReceipt))?.Value ?? string.Empty;
    //return dateOfReceipt.IsNullOrWhiteSpace() ? null : CommonHelper.To<DateTime>(dateOfReceipt);
    public OrderDto() : base()
    { }
    public OrderDto(int orderId) : base(orderId)
    { }
    public OrderDto(Order order) : base(order)
    { }

    [NotColumn, NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => OrderStatus == OrderStatus.Complete")]
    public bool IsComplete => OrderStatus == OrderStatus.Complete;

    public bool HasMeasuringAccess(int? customerId, bool isRevisorUser = false)
        => isRevisorUser || (customerId.HasValue && !IsComplete && (customerId.Value == MeasurementOwnerId || MeasurementOwnerId == 0));

    public bool IsMeasuredAndValid() => Id > 0 && OrderItemDtos.Count > 0 && OrderItemDtos.All(x => x.IsMeasured);
    public bool IsValidMeasuringValues() => OrderItemDtos.Count > 0 && OrderItemDtos.All(x => x.IsValidMeasuringValues());

    public bool IsOtherMeasuringInProgress(int? customerId)
        => customerId.GetValueOrDefault(0) != 0 && OrderItemDtos.Any(oi => oi.IsOtherMeasuringInProgress(customerId));

}