using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Toons;
﻿using FruitBank.Common.Dtos;
using FruitBank.Common.Enums;
using FruitBank.Common.Interfaces;
using LinqToDB;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
using Newtonsoft.Json;

namespace FruitBank.Common.Entities;

[ToonDescription("Base class for pallet measurements with net weight calculation", 
    Purpose = "Technically named 'Pallet' for legacy reasons, but represents a General Measurement Record. It is ALWAYS created for every item. If the product is not measurable, weights are 0 and only TrayQuantity is used.")]
public abstract class MeasuringItemPalletBase : MgEntityBase, IMeasuringItemPalletBase
{
    private double _palletWeight;
    private double _grossWeight;
    private double _tareWeight;

    [NotColumn]
    protected int ForeignItemId;

    [NotColumn]
    [ToonDescription(BusinessRule = "get => ForeignItemId", Constraints = "[#SmartTypeConstraints]")]
    public int ForeignKey => ForeignItemId;

    [ToonDescription(Purpose = "Always recorded, regardless of measurability")]
    public int TrayQuantity { get; set; }
    
    [Column(DataType = DataType.DecFloat)]
    public double TareWeight
    {
        get => _tareWeight;
        set => _tareWeight = double.Round(value, 1);
    }

    [Column(DataType = DataType.DecFloat)]
    [ToonDescription(Purpose = "Weight of the physical pallet if used; 0.0 if goods arrive without a pallet")]
    public double PalletWeight
    {
        get => _palletWeight;
        set => _palletWeight = double.Round(value, 0);
    }

    [NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => GrossWeight - PalletWeight - (TrayQuantity * TareWeight)", Constraints = "[#SmartTypeConstraints], readonly")]
    public double NetWeight
    {
        get => CalculateNetWeight();
        set => throw new Exception($"MeasuringItemPalletBase.NetWeight not set");
    }

    [Column(DataType = DataType.DecFloat, CanBeNull = false)]
    [ToonDescription(Purpose = "Measured gross weight; 0.0 if product is not measurable")]
    public double GrossWeight
    {
        get => _grossWeight;
        set => _grossWeight = double.Round(value, 1);
    }

    public bool IsMeasured { get; set; }

    [SkipValuesOnUpdate]
    public int? CreatorId { get; set; }
    public int? ModifierId { get; set; }

    [SkipValuesOnUpdate] 
    public DateTime Created { get; set; }
    public DateTime Modified { get; set; }

    [NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped, JsonIgnore, System.Text.Json.Serialization.JsonIgnore]
    [ToonDescription(BusinessRule = "get => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted")]
    public virtual MeasuringStatus MeasuringStatus => IsMeasured ? MeasuringStatus.Finnished : Id > 0 ? MeasuringStatus.Started : MeasuringStatus.NotStarted;


    public abstract void SetParentPropToNull();

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

    /// <summary>
    /// Nem lehet nullánál kisebb "Weight" érték és a ShippingId, Quantity nagyobb mint nulla! Megengedőbb mint az IsValidMeasuringValues(bool isMeasurable)...
    /// </summary>
    /// <returns></returns>
    public virtual bool IsValidSafeMeasuringValues()
    {
        return TrayQuantity > 0 && TareWeight >= 0 && PalletWeight >= 0 && NetWeight >= 0 && GrossWeight >= 0;
    }

    /// <summary>
    /// "Szigorúbb" mint az IsValidSafeMeasuringValues()
    /// </summary>
    /// <param name="isMeasurable"></param>
    /// <returns></returns>
    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}]";
    }
}