// Copyright (c) 2026 EzAdmin. All rights reserved.
// Licensed under the EzAdmin Client License. See LICENSE-CLIENT.txt.

using System;
using UnityEngine;

namespace EzAdmin.Client
{

    [Serializable]
    public struct EzSecureInt : IEquatable<EzSecureInt>, IComparable<EzSecureInt>
    {
        [SerializeField] private int _hidden;
        [SerializeField] private int _key;
        [SerializeField] private int _check;
        private int _last;

        public EzSecureInt(int value)
        {
            _key = EzSecureKeys.Next();
            _hidden = value ^ _key;
            _check = EzSecureKeys.Check(value, _key);
            _last = value;
        }

        public int Value
        {
            get
            {
                if (_key == 0 && _hidden == 0 && _check == 0) return 0;
                int value = _hidden ^ _key;
                if (EzSecureKeys.Check(value, _key) != _check)
                {
                    EzAdminAntiCheat.MemoryTampered("whole number");
                    this = new EzSecureInt(_last);
                    return _last;
                }
                _last = value;
                return value;
            }
        }

        public static implicit operator EzSecureInt(int value) => new EzSecureInt(value);
        public static implicit operator int(EzSecureInt secure) => secure.Value;
        public static EzSecureInt operator ++(EzSecureInt a) => new EzSecureInt(a.Value + 1);
        public static EzSecureInt operator --(EzSecureInt a) => new EzSecureInt(a.Value - 1);
        public bool Equals(EzSecureInt other) => Value == other.Value;
        public override bool Equals(object obj) => obj is EzSecureInt other && Equals(other);
        public override int GetHashCode() => Value.GetHashCode();
        public int CompareTo(EzSecureInt other) => Value.CompareTo(other.Value);
        public override string ToString() => Value.ToString();
    }

    [Serializable]
    public struct EzSecureFloat : IEquatable<EzSecureFloat>, IComparable<EzSecureFloat>
    {
        [SerializeField] private int _hidden;
        [SerializeField] private int _key;
        [SerializeField] private int _check;
        private float _last;

        public EzSecureFloat(float value)
        {
            int bits = BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
            _key = EzSecureKeys.Next();
            _hidden = bits ^ _key;
            _check = EzSecureKeys.Check(bits, _key);
            _last = value;
        }

        public float Value
        {
            get
            {
                if (_key == 0 && _hidden == 0 && _check == 0) return 0f;
                int bits = _hidden ^ _key;
                if (EzSecureKeys.Check(bits, _key) != _check)
                {
                    EzAdminAntiCheat.MemoryTampered("number");
                    this = new EzSecureFloat(_last);
                    return _last;
                }
                float value = BitConverter.ToSingle(BitConverter.GetBytes(bits), 0);
                _last = value;
                return value;
            }
        }

        public static implicit operator EzSecureFloat(float value) => new EzSecureFloat(value);
        public static implicit operator float(EzSecureFloat secure) => secure.Value;
        public bool Equals(EzSecureFloat other) => Value.Equals(other.Value);
        public override bool Equals(object obj) => obj is EzSecureFloat other && Equals(other);
        public override int GetHashCode() => Value.GetHashCode();
        public int CompareTo(EzSecureFloat other) => Value.CompareTo(other.Value);
        public override string ToString() => Value.ToString();
    }

    [Serializable]
    public struct EzSecureLong : IEquatable<EzSecureLong>, IComparable<EzSecureLong>
    {
        [SerializeField] private long _hidden;
        [SerializeField] private long _key;
        [SerializeField] private int _check;
        private long _last;

        public EzSecureLong(long value)
        {
            _key = ((long)EzSecureKeys.Next() << 32) | (uint)EzSecureKeys.Next();
            _hidden = value ^ _key;
            _check = EzSecureKeys.Check((int)(value ^ (value >> 32)), (int)_key);
            _last = value;
        }

        public long Value
        {
            get
            {
                if (_key == 0 && _hidden == 0 && _check == 0) return 0;
                long value = _hidden ^ _key;
                if (EzSecureKeys.Check((int)(value ^ (value >> 32)), (int)_key) != _check)
                {
                    EzAdminAntiCheat.MemoryTampered("whole number");
                    this = new EzSecureLong(_last);
                    return _last;
                }
                _last = value;
                return value;
            }
        }

        public static implicit operator EzSecureLong(long value) => new EzSecureLong(value);
        public static implicit operator long(EzSecureLong secure) => secure.Value;
        public bool Equals(EzSecureLong other) => Value == other.Value;
        public override bool Equals(object obj) => obj is EzSecureLong other && Equals(other);
        public override int GetHashCode() => Value.GetHashCode();
        public int CompareTo(EzSecureLong other) => Value.CompareTo(other.Value);
        public override string ToString() => Value.ToString();
    }

    internal static class EzSecureKeys
    {
        private static readonly System.Random Random = new System.Random(Guid.NewGuid().GetHashCode());

        private const int Salt = 0x1F3D5B79;

        internal static int Next()
        {
            lock (Random)
            {
                int key = Random.Next(int.MinValue, int.MaxValue);
                return key == 0 ? 0x5A5A5A5 : key;
            }
        }

        internal static int Check(int value, int key)
        {
            unchecked
            {
                int h = value * 0x2C1B3C6D + Salt;
                h ^= key * 0x297A2D39;
                return (h ^ (h >> 15)) * 0x6A09E667;
            }
        }
    }
}
