using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text.Json.Nodes;

namespace Elwig.Models.Entities {
    [Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
    public class DeliveryPart : IDelivery {

        public const string Dumper = "dumper";
        public const string Pumped = "pumped";
        public const string Box = "box";

        [Column("year")]
        public int Year { get; set; }

        [Column("did")]
        public int DId { get; set; }

        [ForeignKey("Year, DId")]
        public virtual Delivery Delivery { get; private set; } = null!;

        [Column("dpnr")]
        public int DPNr { get; set; }

        [Column("sortid")]
        public required string SortId { get; set; }

        [ForeignKey("SortId")]
        public virtual WineVar Variety { get; private set; } = null!;

        [Column("attrid")]
        public string? AttrId { get; set; }

        [ForeignKey("AttrId")]
        public virtual WineAttr? Attribute { get; private set; }

        [Column("cultid")]
        public string? CultId { get; set; }

        [ForeignKey("CultId")]
        public virtual WineCult? Cultivation { get; private set; }

        [Column("weight")]
        public int Weight { get; set; }

        [Column("kmw")]
        public double Kmw { get; set; }
        [NotMapped]
        public double Oe {
            get => Utils.KmwToOe(Kmw);
            set => Kmw = Utils.OeToKmw(value);
        }

        [Column("qualid")]
        public required string QualId { get; set; }

        [ForeignKey("QualId")]
        public virtual WineQualLevel Quality { get; private set; } = null!;

        [Column("hkid")]
        public required string HkId { get; set; }

        [ForeignKey("HkId")]
        public virtual WineOrigin Origin { get; private set; } = null!;

        [Column("kgnr")]
        public int? KgNr { get; set; }

        [ForeignKey("KgNr")]
        public virtual WbKg? Kg { get; private set; }

        [Column("rdnr")]
        public int? RdNr { get; set; }

        [ForeignKey("KgNr, RdNr")]
        public virtual WbRd? Rd { get; private set; }

        [Column("net_weight")]
        public bool IsNetWeight { get; set; }

        [Column("manual_weighing")]
        public bool IsManualWeighing { get; set; }

        [Column("spl_check")]
        public bool IsSplCheck { get; set; }

        [Column("hand_picked")]
        public bool? IsHandPicked { get; set; }

        [Column("gebunden")]
        public bool? IsGebunden { get; set; }

        [Column("unloading")]
        public string? Unloading { get; set; }
        [NotMapped]
        public bool IsDumper {
            get => Unloading == Dumper;
            set => Unloading = value ? Dumper : Unloading;
        }
        [NotMapped]
        public bool IsPumped {
            get => Unloading == Pumped;
            set => Unloading = value ? Pumped : Unloading;
        }
        [NotMapped]
        public bool IsBox {
            get => Unloading == Box;
            set => Unloading = value ? Box : Unloading;
        }

        [Column("temperature")]
        public double? Temperature { get; set; }

        [Column("acid")]
        public double? Acid { get; set; }

        [Column("scale_id")]
        public string? ScaleId { get; set; }

        [Column("weighing_data")]
        public string? WeighingData { get; set; }
        [NotMapped]
        public (string? Id, int? Gross, int? Tare, int? Net, DateOnly? Date, TimeOnly? Time) WeighingInfo {
            get {
                try {
                    var obj = JsonNode.Parse(WeighingData!)!.AsObject();
                    return (
                        obj["id"]?.AsValue().GetValue<string>(),
                        obj["gross_weight"]?.AsValue().GetValue<int>(),
                        obj["tare_weight"]?.AsValue().GetValue<int>(),
                        obj["net_weight"]?.AsValue().GetValue<int>(),
                        DateOnly.TryParseExact(obj["date"]?.AsValue().GetValue<string>(), "yyyy-MM-dd", out var d) ? d : null,
                        TimeOnly.TryParseExact(obj["time"]?.AsValue().GetValue<string>(), ["HH:mm:ss", "HH:mm"], out var t) ? t : null
                    );
                } catch {
                    return (null, null, null, null, null, null);
                }
            }
        }

        [Column("weighing_reason")]
        public string? WeighingReason { get; set; }

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

        [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public long CTime { get; set; }
        [NotMapped]
        public DateTime CreatedAt {
            get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
            set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
        }

        [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public long MTime { get; set; }
        [NotMapped]
        public DateTime ModifiedAt {
            get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
            set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
        }

        [Column("xtime")]
        public long? XTime { get; set; }
        [NotMapped]
        public DateTime? ExportedAt {
            get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
            set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
        }

        [Column("itime")]
        public long? ITime { get; set; }
        [NotMapped]
        public DateTime? ImportedAt {
            get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
            set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
        }

        [InverseProperty(nameof(DeliveryPartModifier.Part))]
        public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;

        [NotMapped]
        public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);

        [InverseProperty(nameof(PaymentDeliveryPart.DeliveryPart))]
        public virtual PaymentDeliveryPart? Payment { get; private set; }

        [NotMapped]
        public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : "");

        [InverseProperty(nameof(DeliveryPartBucket.Part))]
        public virtual ICollection<DeliveryPartBucket> Buckets { get; private set; } = null!;
    }
}
