using FluentValidation;
using FluentValidation.Results;
using System;
using System.Collections.Generic;
using System.Linq;
using WK5.Core.Models.Lager;

namespace WK5.Core.Validators.Lager
{
    public class RechnungBuchenInputValidator : AbstractValidator<RechnungBuchenInput>
    {
        public RechnungBuchenInputValidator(IValidator<PositionBuchenInput> posValidator, IValidator<ZugangBestellnotizInput> notizenValidator)
        {
            RuleFor(zugang => zugang)
                .Must((zugang) => NummerVorhanden(zugang.Rechnungsnummer, zugang.LieferscheinNummer))
                .WithMessage("Sie müssen entweder eine Lieferschein oder eine Rechnungsnummer angeben");

            RuleFor(zugang => zugang)
                .Custom((zugang, context) =>
                {
                    if (!GesamtsummePasst(zugang.GesamtbetragNetto, zugang.Zusatzkosten, zugang.Positionen))
                    {
                        context.AddFailure(new ValidationFailure(nameof(zugang.GesamtbetragNetto), "Die Gesamtsumme stimmt nicht mit den Positionen und Zusatzkosten überein"));
                    }
                });

            RuleForEach(zugang => zugang.Positionen)
                .SetValidator(posValidator);

            RuleFor(zugang => zugang.Positionen)
                .Must((positionen) => positionen.Count > 0)
                .WithMessage("Der Zugang muss mindestens eine Position enthalten");

            RuleFor(zugang => zugang.BestellungNotiz)
                .SetValidator(notizenValidator);

            RuleFor(x => x.Rechnungsnummer)
                .MaximumLength(40);

            RuleFor(x => x.LieferscheinNummer)
                .MaximumLength(40);

            RuleFor(zugang => zugang)
                .Custom((zugang, context) =>
                {
                    if (zugang.TeilRechnung && zugang.NettoGesamtRechnung <= zugang.GesamtbetragNetto)
                    {
                        context.AddFailure(new ValidationFailure(nameof(zugang.NettoGesamtRechnung), "Die Gesamtsumme der Rechnung muss größer sein als die Teilbuchung selbst"));
                    }
                });

            RuleForEach(pos => pos.Positionen)                    
                .Custom((pos, context) =>
                {
                    foreach (ZugangSeriennummerInput sn in pos.Seriennummern)
                    {
                        if (String.IsNullOrWhiteSpace(sn.Kundenname))
                        {
                            context.AddFailure(new ValidationFailure(nameof(sn.Kundenname), "Der Kundenname darf nicht leer sein"));
                        }                        
                        else if (sn.Kundenname.Length > 100)
                        {
                            context.AddFailure(new ValidationFailure(nameof(sn.Kundenname), "Der Kundenname darf nicht länger als 100 Zeichen sein"));
                        }

                    }                    
                })
                .When(x => x.InternerZugang);
        }

        /// <summary>
        /// Prüft ob die Preise der Positionen und Zusatzkosten mit der Gesamtsumme übereinstimmen.
        /// </summary>
        /// <param name="gesamtsumme"></param>
        /// <param name="positionen"></param>
        /// <returns></returns>
        private bool GesamtsummePasst(decimal gesamtsumme, decimal zusatzkosten, List<PositionBuchenInput> positionen)
        {
            decimal preisPositionen = positionen.Sum(x => x.PreisGesamt);
            decimal preisGesamt = Math.Round(preisPositionen + zusatzkosten, 2);
            return preisGesamt == gesamtsumme;
        }
        /// <summary>
        /// Prüft, ob entweder eine Rechnungsnummer, oder eine Lieferscheinnummer vorhanden ist.
        /// </summary>
        /// <param name="rechnungsNummer"></param>
        /// <param name="lieferscheinNummer"></param>
        /// <returns></returns>
        private bool NummerVorhanden(string? rechnungsNummer, string? lieferscheinNummer)
        {
            return !string.IsNullOrWhiteSpace(rechnungsNummer) || !string.IsNullOrWhiteSpace(lieferscheinNummer);
        }
    }
}
