using Azure.Cosmos;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using TEAMModelOS.Models;
using TEAMModelOS.SDK.Models;
using TEAMModelOS.Models.Dto;

using TEAMModelOS.SDK;
using TEAMModelOS.SDK.DI;
using TEAMModelOS.SDK.Extension;
using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
using TEAMModelOS.SDK.Helper.Common.StringHelper;

namespace TEAMModelOS.Controllers
{
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    //[Authorize(Roles = "IES5")]
    [Route("knowledge")]
    [ApiController]
    //[Authorize]
    public class KnowledgeController : ControllerBase
    {       
        private readonly AzureStorageFactory _azureStorage;
        private readonly AzureCosmosFactory _azureCosmos;
        private readonly SnowflakeId _snowflakeId;
        private readonly DingDing _dingDing;
        private readonly Option _option;

        public KnowledgeController(AzureStorageFactory azureStorage, AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId,DingDing dingDing, IOptionsSnapshot<Option> option)
        {
            _snowflakeId = snowflakeId;
            _azureStorage = azureStorage;
            _azureCosmos = azureCosmos;
            _dingDing = dingDing;
            _option = option?.Value;

        }

        /// <summary>
        /// 删除学校 知识点 或 知识块 代理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="builder"></param>
        /// <returns></returns>
        delegate Task<List<T>> DeleteDelegate<T>(JsonElement request) where T : CosmosEntity;

        /// <summary>
        /// 保存或者更新学校 知识点 或者 知识块 代理
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="schoolBlock"></param>
        /// <param name="_azureCosmos"></param>
        /// <returns></returns>
        delegate Task<string> SchoolSaveOrUpdate<T>(dynamic schoolBlock, AzureCosmosFactory _azureCosmos);

        //  private delegate dynamic SaveOrUpdate(dynamic t);

        /// <summary>
        /// 获取标准知识点
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("find-std-point")]
        public async Task<IActionResult> FindStdPoint(KnowledgeDto request)
        {
            // request.@params.PointParams.TryAdd("PartitionKey", request.lang);
            ResponseBuilder builder = ResponseBuilder.custom();
            List<KnowledgePoint> data = new List<KnowledgePoint>();
            if (request != null && request.PointParams.TryGetValue("SubjectCode", out _))
            {
                List<KnowledgePoint> points = await _azureStorage.FindListByDict<KnowledgePoint>(request.PointParams);
                if (request.Periods.IsNotEmpty())
                {
                    HashSet<KnowledgePoint> knowledges = new HashSet<KnowledgePoint>();
                    foreach (string period in request.Periods)
                    {
                        if (!string.IsNullOrEmpty(period))
                        {
                            knowledges.UnionWith(points.Where(x => x.AdvicePeriodCode.Contains(period)).ToHashSet());
                        }
                    }
                    data = knowledges.OrderBy(x => x.Order).ToList();
                }
                else
                {
                    data = points.OrderBy(x => x.Order).ToList();
                }
            }
            else
            {
                return Ok(new { ResponseCode.PARAMS_ERROR, V = "SubjectCode is null or empty！" });
                 
            }
            //return builder.Data(data).Extend(new Dictionary<string, object> { { "count", data.Count } }).build();
            return Ok(new { data , data.Count});
        }


        /// <summary>
        /// 获取标准的知识块及包含的知识点
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("find-std-block-point")]
        public async Task<IActionResult> FindStdBlockPoint(KnowledgeDto request)
        {
            //request.@params.PointParams.TryAdd("PartitionKey", request.lang);
            ResponseBuilder builder = ResponseBuilder.custom();
            List<BlockPointDto> data = new List<BlockPointDto>();
            if (request != null && request.PointParams.TryGetValue("SubjectCode", out _))
            {
                List<KnowledgeBlockPoint> blockPoints = await _azureStorage.FindListByDict<KnowledgeBlockPoint>(request.PointParams);
                List<KnowledgeBlock> blocks = await _azureStorage.FindListByDict<KnowledgeBlock>(request.PointParams);
                if (request.Periods.IsNotEmpty())
                {
                    HashSet<KnowledgeBlock> knowledges = new HashSet<KnowledgeBlock>();
                    HashSet<KnowledgeBlockPoint> knowledgeBlocks = new HashSet<KnowledgeBlockPoint>();
                    foreach (string period in request.Periods)
                    {
                        if (!string.IsNullOrEmpty(period))
                        {
                            knowledges.UnionWith(blocks.Where(x => x.AdvicePeriodCode.Contains(period)).ToHashSet());
                            knowledgeBlocks.UnionWith(blockPoints.Where(x => x.AdvicePeriodCode.Contains(period)).ToHashSet());
                        }
                    }
                    blocks = knowledges.OrderBy(x => x.Order).ToList();
                    blockPoints = knowledgeBlocks.OrderBy(x => x.Order).ToList();
                }
                List<BlockPointDto> blockPointDtos = new List<BlockPointDto>();
                data = FindTree(ListToTree(blocks, blockPoints), request.BlockId, blockPointDtos);
            }
            else
            {
                //return builder.Error(ResponseCode.PARAMS_ERROR, "SubjectCode is null or empty！").build();
                return Ok(new { ResponseCode.PARAMS_ERROR, V = "SubjectCode is null or empty！" });
            }
            //return builder.Data(data).Extend(new Dictionary<string, object> { { "count", data.Count } }).build();
            return Ok(new { data, data.Count });
        }

        /// <summary>
        /// 获取知识
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("find")]
        public async Task<IActionResult> Find(JsonElement requert)
        {
            var client = _azureCosmos.GetCosmosClient();
            if (!requert.TryGetProperty("code", out JsonElement school_code)) return BadRequest();
            if (!requert.TryGetProperty("type", out JsonElement type)) return BadRequest();
            if (!requert.TryGetProperty("periodId", out JsonElement period)) return BadRequest();
            if (!requert.TryGetProperty("subjectId", out JsonElement subjectCode)) return BadRequest();
            //if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
            List<object> Knowledges = new List<object>();
            List<Dictionary<string, object>> roles = new List<Dictionary<string, object>>();
            /* var response = await client.GetContainer("TEAMModelOSTemp", "Common").ReadItemStreamAsync(id + "", new PartitionKey($"Knowledge-{school_code}"));
             if (response.Status == 200)
             {
                 using var json = await JsonDocument.ParseAsync(response.ContentStream);
                 foreach (var obj in json.RootElement.GetProperty("points").EnumerateArray())
                 {
                     roles.Add(obj.GetString());
                 }
             }*/
            var query = $"select c.id,c.type,c.code, c.name,c.alias,c.subjectId,c.points,c.knowledgeId,c.periodId from c where c.type = {type} and c.periodId = '{period}' and c.subjectId = '{subjectCode}'";
            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
            {
                using var json = await JsonDocument.ParseAsync(item.ContentStream);
                
                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                {
                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                    {
                        Dictionary<string, object> keyValues = new Dictionary<string, object>();
                        foreach (var obj_ in obj.EnumerateObject()) {
                            if ((obj_.Name + "").Equals("type")) {
                                keyValues.Add("type", obj_.Value + "");
                            }
                             if ((obj_.Name+"").Equals("points")) {
                                keyValues.Add("points", obj_.Value +"");
                            }                          
                        }
                        roles.Add(keyValues);
                        Knowledges.Add(obj.ToObject<object>());
                    }
 
                }
            }
            return Ok(new { Knowledges, roles, Knowledges.Count });
            // request.@params.TryAdd("PartitionKey", request.lang);
            /* ResponseBuilder builder = ResponseBuilder.custom();
             List<Knowledge> data = new List<Knowledge>();
             if (StringHelper.getKeyCount(request) > 0)
             {
                 data = await _azureCosmos.FindByDict<Knowledge>(request);
                 data.ForEach(d =>
                 {
                     if (d.type == 1) {
                         if (d.code.Equals("habook#0001"))
                         {
                             d.ownCount = d.points.Count;
                         }
                         else {
                             d.openCount = d.points.Count;
                         }
                     }
                 });
             }
             else
             {
                 return builder.Error(ResponseCode.PARAMS_ERROR, "参数异常！").build();

             }
             return builder.Data(data.OrderBy(m => m.order)).Extend(new Dictionary<string, object> { { "count", data.Count } }).build();*/
        }

        /// <summary>
        /// 保存或更新知识
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("upsert-all")]
        public async Task<IActionResult> UpsertAll(List<Knowledge> request)
        {
            try
            {
                var client = _azureCosmos.GetCosmosClient();
                List<Knowledge> Knowledges = new List<Knowledge>();
                if (request.IsNotEmpty())
                {

                    foreach (Knowledge knowledge in request) {
                        knowledge.code = "Knowledge-" + knowledge.code;
                        Knowledge kno = new Knowledge();
                        if (knowledge.id == null)
                        {
                            knowledge.id = Guid.NewGuid().ToString();
                            kno = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(knowledge, new PartitionKey($"{knowledge.code}"));
                            Knowledges.Add(kno);
                        }
                        else {
                            kno = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(knowledge, knowledge.id, new PartitionKey($"{knowledge.code}"));
                            Knowledges.Add(kno);
                        }
                    }
                    //Knowledges = await SaveOrUpdateKnowledge(request);
                    /*if (ts.Count > 0) builder.Data(ts).Extend(new Dictionary<string, object> { { "count", ts.Count } });
                    else {
                        return Ok(new { ResponseCode.FAILED, V = "失败！" });
                    };  */
                }
                return Ok(new { Knowledges });
            }
            catch (Exception ex)
            {
                await _dingDing.SendBotMsg($"knowledge，{_option.Location}，upsert-all()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
                return BadRequest();
            }
        }


        // TODO 代码优化

        /// <summary>
        /// 删除知识
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("delete")]
        public async Task<IActionResult> Delete(JsonElement request)
        {
            try {
                var client = _azureCosmos.GetCosmosClient();
                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                await client.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Knowledge-{code}"));
                return Ok(id);
            } catch (Exception ex) {
                await _dingDing.SendBotMsg($"knowledge，{_option.Location}，delete()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
                return BadRequest();
            }
            return Ok();
            //ResponseBuilder builder = ResponseBuilder.custom();
            //DeleteDelegate<Knowledge> deleteDelegate1 = Delete<Knowledge>;
            //List<Knowledge> Knowledges = await deleteDelegate1(request);
            //if(Knowledges.Count >0) {
            //    foreach (Knowledge Knowledge in Knowledges)
            //    {
            //        if (Knowledge.type == 1)
            //        {
            //            List<Knowledge> knowledges = await _azureCosmos.FindSQL<Knowledge>("SELECT value(c) FROM c join a in c.points where c.pk='Knowledge'and  Contains(a, '" + Knowledge.id + "') = true ");
            //            List<Resource> Resources = await _azureCosmos.FindSQL<Resource>("SELECT value(c) FROM c join a in c.points where c.pk='Resources' and  Contains(a, '" + Knowledge.id + "') = true ");
            //            //select value(c) from c join A0  in c.points where 1=1  and A0 =  @points0
            //            foreach (Knowledge knowledge in knowledges) knowledge.points.Remove(Knowledge.id);
            //            foreach (Resource Resource in Resources) Resource.points.Remove(Knowledge.id);
            //            await _azureCosmos.SaveOrUpdateAll(Resources);
            //            await _azureCosmos.SaveOrUpdateAll(knowledges);
            //        }
            //    }
            //    return Ok(new { Knowledges });
            //}else
            //{
            //    return Ok(new { erro = ResponseCode.FAILED, v = "删除失败！" });

            //}
            
        }

        /// <summary>
        /// 统计个数
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [ProducesDefaultResponseType]
        [HttpPost("count")]
        public async Task<IActionResult> Count(JsonElement request)
        {
            List<List<List<int>>> sCount = new List<List<List<int>>>();
            //List<List<List<List<int>>>> allCount = new List<List<List<List<int>>>>();
            ResponseBuilder builder = ResponseBuilder.custom();
            request.TryGetProperty("code",out JsonElement code);
            if (StringHelper.getKeyCount(request) > 0) {
                /*List<School> sc = await _azureCosmos.FindByDict<School>(new Dictionary<string, object>() { { "id", code.ToString()}});*/
                List<School> sc = new List<School>();
                var client = _azureCosmos.GetCosmosClient();
                //List<object> schools = new List<object>();
                var query = $"select c.id,c.pk,c.code, c.name,c.region,c.province,c.city,c.timeZone,c.picture,c.size,c.period,c.campuses from c where c.id ='{code}'";
                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
                {
                    using var json = await JsonDocument.ParseAsync(item.ContentStream);

                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                    {
                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                        {
                            sc.Add(obj.ToObject<School>());
                        }
                    }
                }
               /* //查询校本所有知识块
                Dictionary<string, object> scMap = new Dictionary<string, object>
            {
                 { "type", 0},
                 { "status", 1}
            };
                List<Knowledge> knowlegeo = await _azureCosmos.FindByDict<Knowledge>(scMap);*/

                List<Knowledge> knowlegeo = new List<Knowledge>();
                var queryOne = $"select c.id,c.type,c.code, c.name,c.alias,c.subjectId,c.points,c.knowledgeId,c.periodId from c  where c.type = 0 and c.status = 1";
                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryOne, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{code}") }))
                {
                    using var json = await JsonDocument.ParseAsync(item.ContentStream);

                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                    {
                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                        {
                            knowlegeo.Add(obj.ToObject<Knowledge>());
                        }
                    }
                }



                for (int i = 0; i < sc.Count; i++)
                {
                    for (int j = 0; j < sc[i].period.Count; j++)
                    {
                        List<List<int>> pCount = new List<List<int>>();
                        for (int k = 0; k < sc[i].period[j].subjects.Count; k++)
                        {
                            List<int> listCount = new List<int>() { 0, 0 };
                            //查询校本知识块
                            //计数校本知识块与个人知识块
                            int n = 0;
                            int s = 0;
                            for (int m = 0; m < knowlegeo.Count; m++)
                            {
                                if (sc[i].period[j].id.Equals(knowlegeo[m].periodId) &&
                                    sc[i].period[j].subjects[k].id.Equals(knowlegeo[m].subjectId))
                                {
                                    n++;
                                }
                                if (knowlegeo[m].code.Equals("habook#0001") &&
                                    sc[i].period[j].id.Equals(knowlegeo[m].periodId) &&
                                    sc[i].period[j].subjects[k].id.Equals(knowlegeo[m].subjectId))
                                {
                                    s++;
                                }
                            }
                            //册别数量
                            listCount[0] = n;
                            listCount[1] = s;
                            pCount.Add(listCount);
                        }
                        sCount.Add(pCount);
                    }
                }
            }
            //return builder.Data(sCount).build();
            return Ok(sCount);
        }
           


        #region private
        private List<BlockPointDto> ListToTree(List<KnowledgeBlock> blocks, List<KnowledgeBlockPoint> points)
        {
            List<PointDto> pointList = points.ToJsonString().ToObject<List<PointDto>>();
            List<BlockPointDto> blockList = points.ToJsonString().ToObject<List<BlockPointDto>>();
            Dictionary<string, List<PointDto>> pointDict = new Dictionary<string, List<PointDto>>();
            List<string> keys = (pointList.GroupBy(c => c.BlockId).Select(group => group.Key)).ToList();
            foreach (string key in keys)
            {
                pointDict.Add(key, pointList.Where(x => x.BlockId.Equals(key)).ToList());
            }
            //var blockDict = blockList.ToDictionary(n => n.RowKey, n => n);

            var res = from r in blockList group r by r.RowKey into g select g;
            Dictionary<string, BlockPointDto> blockDict = new Dictionary<string, BlockPointDto>();
            foreach (var s in res)
            {
                blockDict.TryAdd(s.First().RowKey, s.First());
            }
            return GetChild(blockList, blockDict, pointDict);
        }
        private List<BlockPointDto> GetChild(List<BlockPointDto> list, Dictionary<string, BlockPointDto> blockDict, Dictionary<string, List<PointDto>> pointDict)
        {
            List<PointDto> empt = new List<PointDto>();
            list = list.OrderBy(m => m.Order).ToList();
            List<BlockPointDto> trees = new List<BlockPointDto>();
            foreach (BlockPointDto node in list)
            {
                node.Type = 0;
                bool fl = pointDict.TryGetValue(node.RowKey, out List<PointDto> points);
                if (fl && points.IsNotEmpty())
                {
                    points.ForEach(m =>
                    {
                        node.Children.Add(new BlockPointDto
                        {
                            PartitionKey = m.PartitionKey,
                            Pid = m.BlockId,
                            RowKey = m.PointId,
                            Type = 1,
                            Order = m.Order,
                            Name = m.Name,
                            SubjectCode = m.SubjectCode,
                            AdvicePeriodCode = m.AdvicePeriodCode,
                            //Timestamp = m.Timestamp
                        });
                    });
                }
                bool flag = blockDict.TryGetValue(node.Pid, out BlockPointDto syllabus);
                if (flag && syllabus != null)
                {
                    syllabus.Children.Add(node);
                }
                else
                {
                    trees.Add(node);
                }
            }
            return trees.OrderBy(x => x.Order).ToList();
        }
        private List<BlockPointDto> FindTree(List<BlockPointDto> trees, String rowKey, List<BlockPointDto> blockPoints)
        {
            if (!string.IsNullOrEmpty(rowKey))
            {
                foreach (BlockPointDto tree in trees)
                {
                    if (tree.RowKey.Equals(rowKey))
                    {
                        blockPoints.Add(tree);
                    }

                    if (null != tree.Children && tree.Children.Count > 0)
                    {
                        FindTree(tree.Children, rowKey, blockPoints);
                    }
                }
            }
            else
            {
                blockPoints = trees;
            }
            return blockPoints;
        }        

        // TODO 代码优化

        private async Task<List<Knowledge>> SaveOrUpdateKnowledge(List<Knowledge> request)
        {
            return new List<Knowledge>();
            //foreach (Knowledge item in request)
            //{
            //    if (item.id == null)
            //    {
            //        item.id = _snowflakeId.NextId() + "";
            //    }
            //    if (item.type == 1 && item.points.Count > 0)
            //    {
            //        List<string> vs = item.points;
            //        foreach (string s in vs)
            //        {
            //            List<Knowledge> knowledges = await _azureCosmos.FindByDict<Knowledge>(new Dictionary<string, object>() { { "id", s } });
            //            foreach (Knowledge knowledge in knowledges)
            //            {
            //                knowledge.points.Remove(item.id);
            //                knowledge.points.Add(item.id);
            //            }
            //            await _azureCosmos.SaveOrUpdateAll(knowledges);
            //        }
            //        item.points.Clear();
            //    }
            //}
            //List<Knowledge> ts = await _azureCosmos.SaveOrUpdateAll(request);
            //return ts;
        }

        #endregion
    }
}
