using System.ComponentModel.DataAnnotations.Schema;
using AyCode.Interfaces.Entities;
using AyCode.Interfaces.TimeStampInfo;
using Newtonsoft.Json;
using static System.Net.Mime.MediaTypeNames;

namespace AyCode.Core.Tests.TestModels;


public abstract partial class BaseEntity : IBaseEntity
{
    /// <summary>
    /// Gets or sets the entity identifier
    /// </summary>
    public int Id { get; set; }

    public override string ToString()
    {
        return $"{GetType().Name} [Id: {Id}]";
    }
}

public interface IBaseEntity //: IEntityInt
{
    public int Id { get; set; }
}

public abstract class MgEntityBase : BaseEntity, IEntityInt
{
    public override string ToString()
    {
        return $"{GetType().Name}; Id: {Id}";
    }
}
public interface IMgStockTaking : IEntityInt, ITimeStampInfo
{
    DateTime StartDateTime { get; set; }
    bool IsClosed { get; set; }
    bool IsReadyForClose();
}

public abstract class MgStockTaking<TStockTakingItem> : MgEntityBase, IMgStockTaking
    where TStockTakingItem : class, IMgStockTakingItem
{
    public DateTime StartDateTime { get; set; }
    public bool IsClosed { get; set; }

    public abstract bool IsReadyForClose();

    public List<TStockTakingItem>? StockTakingItems { get; set; }

    public int Creator { get; set; }
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
}

public interface IMgStockTakingItem : IEntityInt, ITimeStampInfo
{
    int StockTakingId { get; set; }
    int ProductId { get; set; }
    bool IsMeasured { get; set; }
    int OriginalStockQuantity { get; set; }
    int MeasuredStockQuantity { get; set; }
}

public abstract class MgStockTakingItem<TStockTaking, TProduct> : MgEntityBase, IMgStockTakingItem
    where TStockTaking : class, IMgStockTaking
    where TProduct : class, IMgProductDto
{
    public int StockTakingId { get; set; }
    public int ProductId { get; set; }
    public bool IsMeasured { get; set; }
    public int OriginalStockQuantity { get; set; }
    public int MeasuredStockQuantity { get; set; }

    public TStockTaking? StockTaking { get; set; }

    public TProduct? Product { get; set; }

    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }
}


public class StockTaking : MgStockTaking<StockTakingItem>
{
    public override bool IsReadyForClose()
    {
        if (StockTakingItems == null || StockTakingItems.Count == 0) return false;
        return StockTakingItems
            .Where(stockTakingItem => stockTakingItem is { IsRequiredForMeasuring: true, IsInvalid: false })
            .All(x => x.IsMeasured);
    }
}


public class StockTakingItem : MgStockTakingItem<StockTaking, ProductDto>
{
    public bool IsMeasurable { get; set; }

    public double OriginalNetWeight { get; set; }

    public double MeasuredNetWeight { get; set; }

    public int InProcessOrdersQuantity { get; set; }

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public int TotalOriginalQuantity => OriginalStockQuantity + InProcessOrdersQuantity;

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public int QuantityDiff => IsMeasured ? MeasuredStockQuantity - TotalOriginalQuantity : 0;

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public double NetWeightDiff => IsMeasurable && IsMeasured ? double.Round(MeasuredNetWeight - OriginalNetWeight, 1) : 0d;

    public List<StockTakingItemPallet>? StockTakingItemPallets { get; set; }

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public bool IsRequiredForMeasuring => !IsInvalid && (TotalOriginalQuantity != 0 || OriginalNetWeight != 0);

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public bool IsInvalid => TotalOriginalQuantity < 0;

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public string DisplayText
    {
        get
        {
            if (IsInvalid) return $"[HIBA] {Product!.Name}";
            if (IsMeasured) return $"[KÉSZ] {Product!.Name}";
            return IsRequiredForMeasuring ? $"[KÖT] {Product!.Name}" : $"{Product!.Name}";
        }
    }
}

public class StockTakingItemPallet : MeasuringItemPalletBase
{
    public int StockTakingItemId
    {
        get => ForeignItemId;
        set => ForeignItemId = value;
    }

    public StockTakingItem? StockTakingItem { get; set; }

    public override double CalculateNetWeight() => base.CalculateNetWeight();

    public override bool IsValidSafeMeasuringValues()
    {
        return StockTakingItemId > 0 && TrayQuantity >= 0 && TareWeight >= 0 && PalletWeight >= 0 && NetWeight >= 0 && GrossWeight >= 0;
    }

    public override bool IsValidMeasuringValues(bool isMeasurable)
    {
        return StockTakingItemId > 0 && TrayQuantity >= 0 &&
               ((!isMeasurable && NetWeight == 0 && GrossWeight == 0 && PalletWeight == 0 && TareWeight == 0)
                || (isMeasurable && NetWeight >= 0 && GrossWeight >= 0 && PalletWeight >= 0 && TareWeight >= 0));
    }

    public override void SetParentPropToNull() => StockTakingItem = null;
}

public abstract class MeasuringItemPalletBase : MgEntityBase
{
    private double _palletWeight;
    private double _grossWeight;
    private double _tareWeight;

    protected int ForeignItemId;

    public int ForeignKey => ForeignItemId;

    public int TrayQuantity { get; set; }

    public double TareWeight
    {
        get => _tareWeight;
        set => _tareWeight = double.Round(value, 1);
    }

    public double PalletWeight
    {
        get => _palletWeight;
        set => _palletWeight = double.Round(value, 0);
    }

    [System.ComponentModel.DataAnnotations.Schema.NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public double NetWeight
    {
        get => CalculateNetWeight();
        set => throw new Exception($"MeasuringItemPalletBase.NetWeight not set");
    }

    public double GrossWeight
    {
        get => _grossWeight;
        set => _grossWeight = double.Round(value, 1);
    }

    public bool IsMeasured { get; set; }

    public int? CreatorId { get; set; }
    public int? ModifierId { get; set; }

    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }

    public abstract void SetParentPropToNull();
    public void SetForeignKey(int foreignKey) => ForeignItemId = foreignKey;
    public virtual double CalculateNetWeight() => double.Round(GrossWeight - PalletWeight - (TareWeight * TrayQuantity), 1);

    public virtual bool IsValidSafeMeasuringValues()
    {
        return TrayQuantity > 0 && TareWeight >= 0 && PalletWeight >= 0 && NetWeight >= 0 && GrossWeight >= 0;
    }

    public virtual bool IsValidMeasuringValues(bool isMeasurable)
    {
        return TrayQuantity > 0 &&
               ((!isMeasurable && NetWeight == 0 && GrossWeight == 0 && PalletWeight == 0 && TareWeight == 0)
                || (isMeasurable && NetWeight > 0 && GrossWeight > 0 && PalletWeight >= 0 && TareWeight >= 0));
    }

    public bool IsMeasuredAndValid(bool isMeasurable)
    {
        return Id > 0 && IsMeasured && IsValidMeasuringValues(isMeasurable);
    }

    public virtual void SetupCustomItemPalletMeauringValues(bool isMeasurable)
    {
        if (!isMeasurable)
        {
            TareWeight = 0;
            PalletWeight = 0;
            GrossWeight = 0;
        }
        IsMeasured = IsValidMeasuringValues(isMeasurable);
    }

    public override string ToString()
    {
        return $"{base.ToString()} [ForeignItemId: {ForeignItemId}; IsMeasured: {IsMeasured}; PalletWeight: {PalletWeight}; TareWeight: {TareWeight}; Quantity: {TrayQuantity}; NetWeight: {NetWeight}; GrossWeight: {GrossWeight}]";
    }
}

public interface IMgProductDto : IEntityInt
{
    int ProductTypeId { get; set; }
    int ParentGroupedProductId { get; set; }

    string Name { get; set; }
    string ShortDescription { get; set; }
    string FullDescription { get; set; }

    public string Sku { get; set; }

    int WarehouseId { get; set; }
    decimal Price { get; set; }
    int StockQuantity { get; set; }
    decimal ProductCost { get; set; }

    decimal Weight { get; set; }
    decimal Length { get; set; }
    decimal Width { get; set; }
    decimal Height { get; set; }
}

public class ProductDto : MgProductDto
{
    public List<GenericAttributeDto> GenericAttributes { get; set; }

    public ProductDto() :base()
    { }
    public ProductDto(int productId) : base(productId)
    { }
    //public ProductDto(Product product) : base(product)
    //{ }

    [NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    public int AvailableQuantity => StockQuantity;

    public bool HasMeasuringValues() => Id > 0;
}

public abstract class MgProductDto : MgEntityBase, /*Product,*/ IMgProductDto//IModelDtoBase<Product>//, IDiscountSupported<DiscountProductMapping>
{
    //public int Id { get; set; }
    public int ProductTypeId { get; set; }
    public int ParentGroupedProductId { get; set; }
    
    public string Name { get; set; }
    public string ShortDescription { get; set; }
    public string FullDescription { get; set; }

    public string Sku { get; set; }

    public int WarehouseId { get; set; }
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
    public decimal ProductCost { get; set; }

    public decimal Weight { get; set; }
    public decimal Length { get; set; }
    public decimal Width { get; set; }
    public decimal Height { get; set; }

    public bool Deleted { get; set; }

    public bool SubjectToAcl { get; set; }
    public bool LimitedToStores { get; set; }

    protected MgProductDto() :base()
    { }

    protected MgProductDto(int productId)
    {
        Id = productId;
    }
}

public class GenericAttributeDto : MgGenericAttributeDto
{
    
}

public abstract class MgGenericAttributeDto : GenericAttribute
{
    public GenericAttribute CreateMainEntity()
    {
        var mainEntity = Activator.CreateInstance<GenericAttribute>();
        CopyDtoValuesToEntity(mainEntity);

        mainEntity.CreatedOrUpdatedDateUTC = DateTime.UtcNow;
        return mainEntity;
    }

    public void CopyDtoValuesToEntity(GenericAttribute entity)
    {
        entity.Id = Id;
        entity.Key = Key;
        entity.Value = Value;
        entity.EntityId = EntityId;
        entity.KeyGroup = KeyGroup;
        entity.StoreId = StoreId;
        entity.CreatedOrUpdatedDateUTC = CreatedOrUpdatedDateUTC;
    }

    public void CopyEntityValuesToDto(GenericAttribute entity)
    {
        Id = entity.Id;
        Key = entity.Key;
        Value = entity.Value;
        EntityId = entity.EntityId;
        KeyGroup = entity.KeyGroup;
        StoreId = entity.StoreId;
        CreatedOrUpdatedDateUTC = entity.CreatedOrUpdatedDateUTC;
    }
}

public partial class GenericAttribute : BaseEntity
{
    /// <summary>
    /// Gets or sets the entity identifier
    /// </summary>
    public int EntityId { get; set; }

    /// <summary>
    /// Gets or sets the key group
    /// </summary>
    public string KeyGroup { get; set; }

    /// <summary>
    /// Gets or sets the key
    /// </summary>
    public string Key { get; set; }

    /// <summary>
    /// Gets or sets the value
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    /// Gets or sets the store identifier
    /// </summary>
    public int StoreId { get; set; }

    /// <summary>
    /// Gets or sets the created or updated date
    /// </summary>
    public DateTime? CreatedOrUpdatedDateUTC { get; set; }
}