using static PKHeX.Core.LegalityCheckStrings;

namespace PKHeX.Core
{
    /// <summary>
    /// Verifies the <see cref="PKM.Gender"/>.
    /// </summary>
    public sealed class GenderVerifier : Verifier
    {
        protected override CheckIdentifier Identifier => CheckIdentifier.Gender;

        public override void Verify(LegalityAnalysis data)
        {
            var pkm = data.pkm;
            var pi = pkm.PersonalInfo;
            if (pi.Genderless != (pkm.Gender == 2))
            {
                // DP/HGSS shedinja glitch -- only generation 4 spawns
                bool ignore = pkm.Format == 4 && pkm.Species == (int)Species.Shedinja && pkm.Met_Level != pkm.CurrentLevel;
                if (!ignore)
                    data.AddLine(GetInvalid(LGenderInvalidNone));
                return;
            }

            // Check for PID relationship to Gender & Nature if applicable
            int gen = data.Info.Generation;
            if (gen is 3 or 4 or 5)
            {
                // Gender-PID & Nature-PID relationship check
                var result = IsValidGenderPID(data) ? GetValid(LPIDGenderMatch) : GetInvalid(LPIDGenderMismatch);
                data.AddLine(result);

                if (gen != 5)
                    VerifyNaturePID(data);
                return;
            }

            // Check fixed gender cases
            if ((pi.OnlyFemale && pkm.Gender != 1) || (pi.OnlyMale && pkm.Gender != 0))
                data.AddLine(GetInvalid(LGenderInvalidNone));
        }

        private static void VerifyNaturePID(LegalityAnalysis data)
        {
            var pkm = data.pkm;
            var result = pkm.EncryptionConstant % 25 == pkm.Nature
                ? GetValid(LPIDNatureMatch, CheckIdentifier.Nature)
                : GetInvalid(LPIDNatureMismatch, CheckIdentifier.Nature);
            data.AddLine(result);
        }

        private static bool IsValidGenderPID(LegalityAnalysis data)
        {
            var pkm = data.pkm;
            bool genderValid = pkm.IsGenderValid();
            if (!genderValid)
                return IsValidGenderMismatch(pkm);

            // check for mixed->fixed gender incompatibility by checking the gender of the original species
            int original = data.EncounterMatch.Species;
            if (Legal.FixedGenderFromBiGender.Contains(original))
                return IsValidFixedGenderFromBiGender(pkm, original);

            return true;
        }

        private static bool IsValidFixedGenderFromBiGender(PKM pkm, int original)
        {
            var current = pkm.Gender;
            if (current == 2) // shedinja, genderless
                return true;
            var gender = EntityGender.GetFromPID(original, pkm.EncryptionConstant);
            return gender == current;
        }

        private static bool IsValidGenderMismatch(PKM pkm) => pkm.Species switch
        {
            // Shedinja evolution gender glitch, should match original Gender
            (int) Species.Shedinja when pkm.Format == 4 => pkm.Gender == EntityGender.GetFromPIDAndRatio(pkm.EncryptionConstant, 0x7F), // 50M-50F

            // Evolved from Azurill after transferring to keep gender
            (int) Species.Marill or (int) Species.Azumarill when pkm.Format >= 6 => pkm.Gender == 1 && (pkm.EncryptionConstant & 0xFF) > 0x3F,

            _ => false,
        };
    }
}
