using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace BookDemo.QA
{
    public class QuestionAndAnswersCache : BaseCache<Dictionary<int, QuestionAndAnswers>>
    {
        public const string CACHE_KEY = "QuestionAndAnswers";
        public QuestionAndAnswersCache()
            : base(CACHE_KEY)
        {
            base._timeout = 100;
        }

        /// <summary>
        /// 检查是否有当前全局缓存关键字存在于当前缓村信息中，
        /// 如果有，则添加关键字
        /// </summary>
        /// <param name="qa">QuestionAndAnswers实例</param>
        private void CheckGlobalKeywords(QuestionAndAnswers qa)
        {
            //便利全局缓存关键字
            CheckAllKeywordsInQuestion(qa, base.Data[0].Keywords);
        }

        /// <summary>
        /// 检查是否所有的关键字都在问题中
        /// </summary>
        /// <param name="qa">QuestionAndAnswers实例</param>
        /// <param name="keywords">关键字列表</param>
        /// <returns></returns>
        private bool CheckAllKeywordsInQuestion(QuestionAndAnswers qa, List<string> keywords)
        {
            bool hasAllKeywords = true;//是否全部包含匹配关键字
            //便利全当前存关键字
            foreach (string kw in keywords)
            {
                if (!qa.Keywords.Contains(kw))
                {
                    string kwUpper = kw.ToUpper();//转为大写
                    /*如果QuestionAndAnswers项中不存在改关键字，
                      则搜索该项“提问”、“回答”数据中是否存在关键字*/
                    bool keywordExist = (qa.Question.Text.ToUpper().Contains(kwUpper)
                                         || qa.Question.Title.ToUpper().Contains(kwUpper)
                                         || qa.Question.Text.ToUpper().Contains(kwUpper)
                                         || (qa.Answers.Count > 0 ? qa.Answers[0].Text.ToUpper().Contains(kwUpper) : false));
                    if (keywordExist)
                    {
                        qa.Keywords.Add(kw);//如果关键字存在，则加入qa的关键字列表
                    }
                    else
                    {
                        hasAllKeywords = false;//如果不存在，则标注“全部匹配”失败
                    }
                }
            }
            return hasAllKeywords;//返回匹配结果
        }


        public override Dictionary<int, QuestionAndAnswers> Data
        {
            get { return base.Data; }
            set { throw new Exception("不允许给Data赋值！"); }
        }

        public override Dictionary<int, QuestionAndAnswers> Update()
        {
            Dictionary<int, QuestionAndAnswers> data = new Dictionary<int, QuestionAndAnswers>();
            //建立一个ID=0的记录，用于记录所有的关键字Keyword
            QuestionAndAnswers defaultKeywordsData = new QuestionAndAnswers
            {
                Answers = new List<QAAnswer>(),
                Question = new QAQuestion(),
                Keywords = new List<string>(),
            };
            data.Add(0, defaultKeywordsData);
            base.SetData(data, base._timeout, null, null);
            return data;
        }

        /// <summary>
        /// 通过ID获取提问、回答数据
        /// </summary>
        /// <param name="id">提问ID</param>
        /// <returns></returns>
        public QuestionAndAnswers GetQuestionAndAnswersItem(int id)
        {
            //先从缓存中查找
            if (base.Data.ContainsKey(id))
            {
                //如果缓存中存在，返回缓存中的信息
                return base.Data[id];
            }
            else
            {
                //缓存中不存在，查询数据库
                QuestionAndAnswers questionAndAnsers = GetDatabaseQuestionAndAnswersItem(id);
                //加入缓存
                if (questionAndAnsers != null)
                {
                    this.Data.Add(id, questionAndAnsers);
                }
                return questionAndAnsers;//返回数据
            }
        }

        /// <summary>
        /// 获取数据库中的信息
        /// </summary>
        /// <param name="id">提问ID</param>
        /// <returns></returns>
        public QuestionAndAnswers GetDatabaseQuestionAndAnswersItem(int id)
        {
            BookDemoEntities ctx = new BookDemoEntities();
            QAQuestion question = ctx.QAQuestion
                                    .Include("QAAnswer")
                                    .Include("QAAnswer.QAUserInfo")
                                    .Include("QACatalog")
                                    .Include("QAUserInfo")
                                    .Where(q => q.Id == id).FirstOrDefault();
            if (question == null)
            {
                //数据库中不存在，返回
                return null;
            }
            else
            {
                //数据库中有记录
                //查询所有回答
                List<QAAnswer> answers = (from a in question.QAAnswer
                                          orderby a.BestAnswer descending, a.Id ascending
                                          select a
                                          ).ToList();
                //创建新的QuestionAndAnsers实体
                QuestionAndAnswers questionAndAnsers = new QuestionAndAnswers
                {
                    Question = question,
                    Answers = answers,
                    Keywords = new List<string>()
                };
                //添加关键字
                CheckGlobalKeywords(questionAndAnsers);

                return questionAndAnsers;//返回questionAndAnsers对象
            }
        }

        /// <summary>
        /// 同步数据库和缓存的指定项
        /// </summary>
        /// <param name="id">提问ID</param>
        /// <returns></returns>
        public QuestionAndAnswers UpdateQuestionAndAnswersItem(int id)
        {
            QuestionAndAnswers qa = GetDatabaseQuestionAndAnswersItem(id);
            if (qa != null)
            {
                base.Data[id] = qa;
            }
            return qa;
        }

        /// <summary>
        /// 根据关键字搜索查询结果
        /// </summary>
        /// <param name="keywords">关键字列表</param>
        /// <returns></returns>
        public List<QuestionAndAnswers> SearchQuestionByKeyWord(List<string> keywords)//TODO:可以创建一个分页功能
        {
            if (keywords == null || keywords.Count == 0)
            {
                return new List<QuestionAndAnswers>();
            }

            //为了保证效率，可以在这里限定关键字的个数，这一步也能在页面上做
            //TODO:同时可以在页面上限定整个搜索字符串的长度
            int limitKeywords = 4;//限定关键字个数
            if (keywords.Count > 4)
            {
                //移除限定关键字个数之后的所有关键字
                keywords.RemoveRange(limitKeywords - 1, keywords.Count - (limitKeywords - 1));
            }

            keywords.RemoveAll(k => k.Trim().Equals(""));//去除空格,TODO:如果传入时已做判断，这里为了提高效率可以省略
            keywords = keywords.Distinct().ToList();//去除重复的关键字
            //string keywordsInCache = string.Join(" ", keywords.ToArray());//储存在全局关键字中的
            if (keywords.Count == 0)
            {
                return new List<QuestionAndAnswers>();
            }

            //从缓存关键字集合中查找，如果所有关键字都存在，则从缓存中查找，否则从数据库查找
            bool searchFromCache = true;//是否从缓存中查找
            foreach (string kw in keywords)
            {
                if (!Data[0].Keywords.Contains(kw))
                {
                    searchFromCache = false;//不从缓存中查找，从数据库查找
                    Data[0].Keywords.Add(kw);//把新的关键字放入缓存中
                }
            }

            List<QuestionAndAnswers> qaList = null;
            if (searchFromCache)
            {
                foreach (var kw in keywords)
                {
                    if (qaList == null)
                    {
                        qaList = (from q in base.Data
                                  where q.Key > 0
                                  && q.Value.Keywords.Contains(kw)
                                  select q.Value).ToList();
                    }
                    else
                    {
                        qaList = (from q in qaList
                                  where q.Keywords.Contains(kw)
                                  select q).ToList();
                    }
                }

                /*TODO:如果结果为空，可以考虑从数据库再次搜索，
                  但是这样做会在数据比较少，命中率不高的情况下增加服务器负担*/
            }
            else
            {
                //缓存中不存在，查询数据库
                //这里直接使用SQL语句提高效率，也可以使用存储过程（sp_SearchQuestion）进一步提高效率
                BookDemoEntities ctx = new BookDemoEntities();
                qaList = new List<QuestionAndAnswers>();
                string keyword = string.Join("+", keywords.ToArray());//组合“格式化”关键字，为了传入SQL Server存储过程
                List<QAQuestion> questionIdList = ctx.SpSearchQuestion(keyword).ToList();//获取满足任一关键字的提问ID列表
                /* 说明：这里之所以这么做，而不是直接从数据库获取同时满足所有
                 * 关键字的记录，是出于使用缓村查询更加高效的角度考虑，
                 * 牺牲第一次查询，可以在以后的查询中更加高效*/

                //放入缓存
                foreach (var item in questionIdList)
                {
                    if (!Data.ContainsKey(item.Id))
                    {
                        //从数据库中查找完整信息 TODO:也可以在存储过程中一次完成
                        QuestionAndAnswers newQA = GetDatabaseQuestionAndAnswersItem(item.Id);
                        if (newQA != null)
                        {
                            //newQA.Keywords = keywords;
                            CheckGlobalKeywords(newQA);//检查全局关键字
                            if (CheckAllKeywordsInQuestion(newQA, keywords))
                            {
                                qaList.Add(newQA);//所有关键字都符合，添加到qaList结果集
                            }
                            Data.Add(newQA.Question.Id, newQA);
                        }
                    }
                    else
                    {
                        //缓存中已存在该提问，把关键字添加到缓存关键字列表中
                        QuestionAndAnswers oldQA = Data[item.Id];
                        if (CheckAllKeywordsInQuestion(oldQA, keywords))
                        {
                            qaList.Add(oldQA);//所有关键字都符合，添加到qaList结果集
                        }
                    }
                }
            }
            //返回结果列表，按关键字个数，倒序排列
            //TODO:这里也可以给页面（或者“问题”数据）设置一个Rank参数，效率更高
            return qaList.OrderByDescending(q => q.Keywords.Count).ToList();
        }
    }
}