// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Net;
using System.Text;

using CommunityToolkit.Mvvm.ComponentModel;
using HostsUILib.Helpers;

namespace HostsUILib.Models
{
#if !TESTONLY
    [Microsoft.UI.Xaml.Data.Bindable]
#endif
    public partial class Entry : ObservableObject
    {
        private static readonly char[] _spaceCharacters = new char[] { ' ', '\t' };

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(Valid))]
        [NotifyPropertyChangedFor(nameof(IsAddressValid))]
        private string _address;

        partial void OnAddressChanged(string value)
        {
            if (ValidationHelper.ValidIPv4(value))
            {
                Type = AddressType.IPv4;
            }
            else if (ValidationHelper.ValidIPv6(value))
            {
                Type = AddressType.IPv6;
            }
            else
            {
                Type = AddressType.Invalid;
            }
        }

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(Valid))]
        [NotifyPropertyChangedFor(nameof(IsHostsValid))]
        private string _hosts;

        partial void OnHostsChanged(string value)
        {
            SplittedHosts = value.Split(' ');
        }

        [ObservableProperty]
        [NotifyPropertyChangedFor(nameof(Valid))]
        private string _comment;

        [ObservableProperty]
        private bool _active;

        [ObservableProperty]
        private bool? _ping;

        [ObservableProperty]
        private bool _pinging;

        [ObservableProperty]
        private bool _duplicate;

        public bool Valid => Validate(true);

        public bool IsAddressValid => ValidateAddressField();

        public bool IsHostsValid => ValidateHostsField(true);

        public string Line { get; private set; }

        public AddressType Type { get; private set; }

        public string[] SplittedHosts { get; private set; }

        public int Id { get; set; }

        public Entry()
        {
        }

        public Entry(int id, string line)
        {
            Id = id;
            Line = line.Trim();
            Parse();
        }

        public Entry(int id, string address, string hosts, string comment, bool active)
        {
            Id = id;
            Address = address.Trim();
            Hosts = hosts.Trim();
            Comment = comment.Trim();
            Active = active;
        }

        public void Parse()
        {
            Active = !Line.StartsWith("#", StringComparison.InvariantCultureIgnoreCase);

            var lineSplit = Line.TrimStart(' ', '#').Split('#');

            if (lineSplit.Length == 0)
            {
                return;
            }

            var addressHost = lineSplit[0];

            var addressHostSplit = addressHost.Split(_spaceCharacters, StringSplitOptions.RemoveEmptyEntries);
            var hostsBuilder = new StringBuilder();
            var commentBuilder = new StringBuilder();

            for (var i = 0; i < addressHostSplit.Length; i++)
            {
                var element = addressHostSplit[i].Trim();

                if (i == 0 && IPAddress.TryParse(element, out var _) && (element.Contains(':') || element.Contains('.')))
                {
                    Address = element;
                }
                else
                {
                    if (hostsBuilder.Length > 0)
                    {
                        hostsBuilder.Append(' ');
                    }

                    hostsBuilder.Append(element);
                }
            }

            Hosts = hostsBuilder.ToString();

            for (var i = 1; i < lineSplit.Length; i++)
            {
                if (commentBuilder.Length > 0)
                {
                    commentBuilder.Append('#');
                }

                commentBuilder.Append(lineSplit[i]);
            }

            Comment = commentBuilder.ToString().Trim();
        }

        public Entry Clone()
        {
            return new Entry
            {
                Line = Line,
                Address = Address,
                Hosts = Hosts,
                Comment = Comment,
                Active = Active,
            };
        }

        private bool ValidateAddressField()
        {
            return Type != AddressType.Invalid;
        }

        private bool ValidateHostsField(bool validateHostsLength)
        {
            return ValidationHelper.ValidHosts(Hosts, validateHostsLength);
        }

        public bool Validate(bool validateHostsLength)
        {
            if (Equals("102.54.94.97", "rhino.acme.com", "source server") || Equals("38.25.63.10", "x.acme.com", "x client host"))
            {
                return false;
            }

            return ValidateAddressField() && ValidateHostsField(validateHostsLength);
        }

        private bool Equals(string address, string hosts, string comment)
        {
            return string.Equals(Address, address, StringComparison.Ordinal)
                && string.Equals(Hosts, hosts, StringComparison.Ordinal)
                && string.Equals(Comment, comment, StringComparison.Ordinal);
        }
    }
}
