using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Console = HyperCube.Utils.AdvConsole;

namespace HyperCube.Models
{
    public enum ArticleStatus {
        [Display(Name = "Новый")]
        New = 0,
        [Display(Name = "Сохранен")]
        Saved,
        [Display(Name = "Ожидает верификации")]
        AwatingVerify,
        [Display(Name = "Верифицируется")]
        Verifying,
        [Display(Name = "Верифицирован")]
        Verified,
        [Display(Name = "Отклонен")]
        Rejected,
        [Display(Name = "Удален")]
        Deleted }

    public class ArticleModel : ICloneable
    {
        public int ID { get; set; }
        [Required]
        public string Filename { get; set; }
        [Required]
        public string HashSum { get; set; }
        public string FilenameReal { get { return ID + "_" + Filename; } }
        [Required]
        public string Name { get; set; }
        [Required]
        public DateTime PublishDate { get; set; } = DateTime.Now.Date;
        [Required]
        public string Authors { get; set; }
        [Required]
        public string Keywords { get; set; }
        public string Tags { get; set; }
        public int CharCount { get; set; }
        [Required]
        public string Annotation { get; set; }
        public string Text { get; set; }

        public ArticleStatus Status { get; set; } = ArticleStatus.New;

        /// fields for recognited text
        public string NounGroups { get; set; }
        public string Entities { get; set; }
        public string Morph { get; set; }
        public string Keywords1 { get; set; }
        public string Keywords2 { get; set; }

        public float[] weights = new float[] {1,1,1,1,1,1,1};

        /// collection for history of verification
        public Dictionary<int, VerificationPoint> VerificationHistory { get; set; } = new();
        public int CalcPValue()
        {            
            var P1 = CharCount;
            var P2 = Keywords.Split(",").Length;
            if (Tags == null)
                Tags = "";
            var P3 = Tags.Split(",").Length;
            var P4 = 1; //параметр качества аннотации, измеряется семантическим алгоритмом Системы
            var P5 = 1; //параметр рейтинга издания, численно равен единице, делённой на номер квартиля издания, при отсутствии квартиля равен 0,2
            var P6 = 1; //параметр страны, численно равен единице, делённой на номер группы рейтинга страны
            var P7 = 1; //параметр ценности нарратива, измеряется семантическим алгоритмом Системы.
            return (int) Math.Floor(P1 * weights[0] + P2 * weights[1] + P3 * weights[2] + P4 * weights[3] + P5 * weights[4] + P6 * weights[5] + P7 * weights[6]);
        }

        public int? Rating
        {
            get { return rating; }
            set
            {
                rating = value;
                if (rating < 1) rating = 1;
                if (rating > 5) rating = 5;
            }
        }

        private int? rating;
        private string initiatorUUID = "";

        public static ArticleModel Find(int id)
        {
            if (id > 0) {

                if (AppData.Articles.ContainsKey(id))
                    return AppData.Articles[id];
            }
            return null;
        }

        public async Task<int> GetEditsCount(string acc_id = "")
        {
            MySQLConnector dbCon = MySQLConnector.Instance();

            string stringSQL = $"SELECT COUNT(*) " +
                $"FROM articles_edit_log ";
            if (acc_id.Length < 36)
                stringSQL += $"WHERE article_id={this.ID}";            
            else
                stringSQL += $"WHERE article_id={this.ID} AND acc_id='{acc_id}'";

            int count = await dbCon.SQLSelectCount(stringSQL);
            
            return count;
        }

        public async Task<string> GetInitiatorUUID(ArticleStatus status)
        {
            if (initiatorUUID.Length < 36)
            {
                Console.WriteLine($"initiatorUUID {initiatorUUID} is null, getting it from DB");

                MySQLConnector dbCon = MySQLConnector.Instance();

                string stringSQL = $"SELECT acc_id " +
                    $"FROM articles " +
                    $"JOIN actions_history ON actions_history.article_id = articles.id " +
                    $"WHERE articles.id={this.ID} AND action_type={(int)status}";

                initiatorUUID = await dbCon.SQLSelectUUID(stringSQL);                               
            }
            else
                Console.WriteLine("initiatorUUID already set");

            Console.WriteLine($"initiatorUUID found {initiatorUUID}");
            return initiatorUUID;
        }

        public async Task<Dictionary<int, VerificationPoint>> GetVerificationHistory(string acc_uuid)
        {
            Console.WriteLine($"GetVerificationHistory for article [{this.ID}]");

            //Dictionary<int, VerificationPoint> verificationHistory = new();
            VerificationPoint verificationPoint;

            var vps = await MySQLConnector.Instance().SQLSelectComplex($"SELECT * FROM articles_verification WHERE article_id='{this.ID}'"); //AND acc_id='{acc_uuid}'
            if (vps.Count > 0)
            {
                foreach (var vp in vps)
                {
                    verificationPoint = new()
                    {
                        ID = Convert.ToInt32(vp["id"]),
                        RulesViolation = Convert.ToBoolean(vp["rules_violation"]),
                        NonExpert = Convert.ToBoolean(vp["nonexpert"]),
                        AdditionalVerificationRequired = Convert.ToBoolean(vp["additional_verification_required"]),
                        Rejected = Convert.ToBoolean(vp["rejected"]),
                        RejectReason = Convert.ToString(vp["reject_reason"]),
                        Tags = Convert.ToString(vp["tags"]),
                        DateAdd = Convert.ToDateTime(vp["date_add"])
                    };
                    Console.WriteLine($"add verificationPoint. id: {verificationPoint.ID}, rules_violation: {verificationPoint.RulesViolation}, date_add: {verificationPoint.DateAdd}");
                    VerificationHistory.Add(verificationPoint.ID, verificationPoint);
                }
            }
            else
                Console.WriteLine($"Article [{this.ID}] has no history yet.");

            return VerificationHistory;
        }

        public async Task SaveLastVerificationPoint(VerificationPoint verificationPoint, string acc_uuid)
        {
            //Console.WriteLine($"SaveVerificationPoint");            

            int rules_violation = Convert.ToInt32(verificationPoint.RulesViolation);
            int nonexpert = Convert.ToInt32(verificationPoint.NonExpert);
            int additional_verification_required = Convert.ToInt32(verificationPoint.AdditionalVerificationRequired);
            int rejected = Convert.ToInt32(verificationPoint.Rejected);

            //if (verificationPoint.ID == 0) ///new point
            //{
                Console.WriteLine($"SaveVerificationPoint. New point to save");

                long id = await MySQLConnector.Instance().SQLInsert($"INSERT INTO articles_verification " +
                                $"(article_id, acc_id, rules_violation, nonexpert, additional_verification_required, rejected, reject_reason, tags) " +
                                $"VALUES ('{this.ID}','{acc_uuid}','{rules_violation}', '{nonexpert}', '{additional_verification_required}', " +
                                $"'{rejected}', '{verificationPoint.RejectReason}', '{verificationPoint.Tags}')");
                if (id > 0)
                {
                    verificationPoint.ID = (int)id;
                    VerificationHistory.Add(verificationPoint.ID, verificationPoint);

                    Console.WriteLine($"SaveVerificationPoint. Point saved, id: {id}");
                }
                else
                    Console.WriteLine($"SaveVerificationPoint. Something went wrong, id < 1");
            //}
            //else ///existed
            //{
            //    Console.WriteLine($"SaveVerificationPoint. Existed point to save, id: {verificationPoint.ID}");

            //    await MySQLConnector.Instance().SQLInsert($"UPDATE articles_verification SET " +                                    
            //                        $"acc_id='{acc_uuid}', rules_violation='{rules_violation}', nonexpert='{nonexpert}', additional_verification_required={additional_verification_required}, " +
            //                        $"rejected={rejected}, reject_reason={verificationPoint.RejectReason}, tags={verificationPoint.Tags}) " +
            //                        $"WHERE id={verificationPoint.ID}");
            //}
        }

        public object Clone()
        {
            return MemberwiseClone();
        }
    }

    public class VerificationPoint
    {
        public int ID;
        public bool RulesViolation;
        public bool NonExpert;
        public bool AdditionalVerificationRequired;
        public bool Rejected;
        public string RejectReason;
        public string Tags;
        public DateTime DateAdd;        

        public VerificationPoint()
        {
        }        
    }
}