using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Windows.Media;
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;

namespace Elwig.Models.Entities {
    [Table("delivery"), PrimaryKey("Year", "DId"), Index("DateString", "ZwstId", "LNr", IsUnique = true), Index("LsNr", IsUnique = true)]
    public class Delivery {
        [Column("year")]
        public int Year { get; set; }

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

        [Column("date")]
        public required string DateString { get; set; }
        [NotMapped]
        public DateOnly Date {
            get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
            set => DateString = value.ToString("yyyy-MM-dd");
        }

        [Column("time")]
        public string? TimeString { get; set; }
        [NotMapped]
        public TimeOnly? Time {
            get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
            set => TimeString = value?.ToString("HH:mm:ss");
        }

        [NotMapped]
        public DateTime DateTime {
            get => Date.ToDateTime(Time ?? TimeOnly.MinValue);
            set {
                Date = DateOnly.FromDateTime(value);
                Time = TimeOnly.FromDateTime(value);
            }
        }

        [Column("zwstid")]
        public required string ZwstId { get; set; }

        [ForeignKey("ZwstId")]
        public virtual Branch Branch { get; private set; } = null!;

        [Column("lnr")]
        public int LNr { get; set; }

        [Column("lsnr")]
        public required string LsNr { get; set; }

        [Column("mgnr")]
        public int MgNr { get; set; }

        [ForeignKey("MgNr")]
        public virtual Member Member { get; private set; } = null!;

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

        [NotMapped]
        public string[] Comments => [.. Parts.Select(p => p.Comment).Prepend(Comment).Where(c => c != null).Cast<string>()];
        [NotMapped]
        public string CommentsString => string.Join(" / ", Comments);

        [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();
        }

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

        [InverseProperty(nameof(DeliveryPart.Delivery))]
        public virtual ICollection<DeliveryPart> Parts { get; private set; } = null!;
        [NotMapped]
        public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));

        [NotMapped]
        public Predicate<DeliveryPart>? PartFilter { get; set; }

        public int Weight => Parts.Select(p => p.Weight).Sum();
        public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();

        public IEnumerable<RawVaribute> Vaributes => Parts
            .GroupBy(p => (p.SortId, p.AttrId, p.CultId))
            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
            .Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
        public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
            .GroupBy(p => (p.SortId, p.AttrId, p.CultId))
            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
            .Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
        public string VaributeString => string.Join(", ", Vaributes);
        public string FilteredVaributeString => string.Join(", ", FilteredVaributes);

        public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
        public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();

        public IEnumerable<string> Modifiers => Parts
            .SelectMany(p => p.Modifiers)
            .Select(m => m.Name)
            .Distinct()
            .Order();
        public IEnumerable<string> FilteredModifiers => FilteredParts
            .SelectMany(p => p.Modifiers)
            .Select(m => m.Name)
            .Distinct()
            .Order();
        public string ModifiersString => string.Join(" / ", Modifiers);
        public string FilteredModifiersString => string.Join(" / ", FilteredModifiers);

        public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
        public double FilteredKmw => Utils.AggregateDeliveryPartsKmw(FilteredParts);

        public double Oe => Utils.KmwToOe(Kmw);
        public double FilteredOe => Utils.KmwToOe(FilteredKmw);

        public int SearchScore(IEnumerable<string> keywords) {
            var list = new string?[] {
                LsNr, Time?.ToString("HH:mm"),
                Member.Name, Member.MiddleName, Member.GivenName, Member.BillingAddress?.FullName,
                Comment
            }.ToList();
            list.AddRange(Parts.Select(p => p.Comment).Distinct());
            return Utils.GetSearchScore(list, keywords);
        }
    }
}
