using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;

using FMSAdmin.Data;
using FMSAdmin.Helpers;
using FMSAdmin.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using FMSAdmin.Models;

namespace FMSAdmin.Services {
    public class SitemapService {
        private readonly ILogger<SitemapService> _logger;
        private readonly FMSContext _context;
        private readonly StorageHelper _storage;

        public SitemapService(
            ILogger<SitemapService> logger,
            FMSContext context,
            StorageHelper storage) {
            _logger = logger;
            _context = context;
            _storage = storage;
        }

        public void Save(Sitemap data) {
            var persist = _context.Sitemap.Find(data.SitemapId);
            if (persist == null) {
                var siblings = _context.Sitemap.AsNoTracking().Where(x => x.ParentId == data.ParentId);
                if (siblings.Any()) {
                    data.Position = siblings.DefaultIfEmpty().Max(x => x.Position) + 1;
                }

                if (data.ParentId != null) {
                    var parent = _context.Sitemap.AsNoTracking()
                            .Where(x => x.SitemapId == data.ParentId).FirstOrDefault();
                    if (parent != null) {
                        data.Depth = parent.Depth + 1;
                    }
                    if (data.Depth > 4) {
                        throw new ServiceException("최대 4뎁스까지 지원합니다.");
                    }
                }
                data.Created = DateTime.Now;
                _context.Sitemap.Add(data);
                _context.SaveChanges();
            } else {
                persist.Name = data.Name;
                persist.Path = data.Path;
                persist.Icon = data.Icon;
                persist.Action1 = data.Action1;
                persist.Action2 = data.Action2;
                persist.Action3 = data.Action3;
                persist.Action4 = data.Action4;
                persist.Action5 = data.Action5;
                persist.Memo = data.Memo;
                persist.Updated = DateTime.Now;
                _context.Sitemap.Update(persist);
                _context.SaveChanges();
            }
        }

        public Sitemap Get(int id) {
            var query = _context.Sitemap.Single(x => x.SitemapId == id);
            return query;
        }

        public IQueryable<Sitemap> Gets(int id) {
            var query = _context.Sitemap.Where(x => x.SitemapId == id);
            return query;
        }

        public IQueryable<Sitemap> GetAll() {
            var query = _context.Sitemap/*.AsNoTracking()*/;
            return query;
        }

        public void Delete(int id) {
            var data = _context.Sitemap.Single(x => x.SitemapId == id);

            var siblings = GetAll().Where(x => x.Type == data.Type && x.Parent == data.Parent);
            if (siblings.Count() > 0) {
                var pos = 0;
                foreach (var sib in siblings.Where(x => x != data).OrderBy(x => x.Position)) {
                    sib.Position = pos++;
                }
            }

            // 하위메뉴 탐색
            Func<Sitemap, bool> loopDelete = null;
            loopDelete = (item) => {
                foreach (var child in item.Childs) {
                    loopDelete(child);
                }
                _context.Sitemap.Remove(item);
                return true;
            };
            loopDelete(data);

            _context.SaveChanges();
        }


        public void Up(Sitemap data) {
            var siblings = GetAll().Where(x => x.Type == data.Type && x.Parent == data.Parent);
            var upnode = siblings.Where(x => x.Position < data.Position).OrderByDescending(x => x.Position).FirstOrDefault();
            if (upnode != null) {
                var uppos = upnode.Position;
                upnode.Position = data.Position;
                data.Position = uppos;
            } else {
                data.Position = 0;
            }
            _context.SaveChanges();

            var pos = 0;
            foreach (var node in siblings.OrderBy(x => x.Position)) {
                node.Position = pos++;
            }
            _context.SaveChanges();
        }

        public void Down(Sitemap data) {
            var siblings = GetAll().Where(x => x.Type == data.Type && x.Parent == data.Parent);
            var downnode = siblings.Where(x => x.Position > data.Position).OrderBy(x => x.Position).FirstOrDefault();
            if (downnode != null) {
                var uppos = downnode.Position;
                downnode.Position = data.Position;
                data.Position = uppos;
            } else {
                data.Position = siblings.Count();
            }
            _context.SaveChanges();

            var pos = 0;
            foreach (var node in siblings.OrderBy(x => x.Position)) {
                node.Position = pos++;
            }
            _context.SaveChanges();
        }

        public void Upload(SitemapType type, Sitemap[] list) {
            var siblings = GetAll().Where(x => x.Type == type && x.ParentId == null);
            var posNullable = siblings.DefaultIfEmpty().Max(x => (int?)x.Position);
            var pos = (posNullable ?? 0) + 1;

            _logger.LogInformation("pos : " + pos);
            _logger.LogInformation(list.Dump());

            // 하위메뉴 탐색
            Func<Sitemap, int, bool> loop = null;
            loop = (item, depth) => {
                item.Type = type;
                item.Depth = depth;
                item.Created = DateTime.Now;
                if (depth >= 4) {
                    item.Childs = null;
                    return true;
                }
                var pos = 0;
                foreach (var child in item.Childs) {
                    child.Position = pos++;
                    loop(child, depth + 1);
                }
                return true;
            };

            foreach (var data in list) {
                data.Position = pos++;
                loop(data, 0);
            }

            _context.Sitemap.AddRange(list);
            _context.SaveChanges();
        }

    }
}