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 UserService {
        private readonly ILogger<UserService> _logger;
        private readonly FMSContext _context;
        private readonly StorageHelper _storage;

        private readonly AppSettings _appSettings;

        public UserService(
            ILogger<UserService> logger,
            FMSContext context,
            IOptions<AppSettings> appSettings,
            StorageHelper storage) {
            _logger = logger;
            _context = context;
            _appSettings = appSettings.Value;
            _storage = storage;
        }

        public void Delete(object id) {
            try {
                CmUser data = _context.CmUser.Find(id);
                data.IsUse = false;
                _context.SaveChanges();
            } catch (Exception ex) {
                throw ex;
            }
        }

        public void Delete(string[] list) {
            var query = from x in _context.CmUser
                        where list.Contains(x.UserId)
                        select x;
            foreach (var item in query) {
                item.IsUse = false;
            }
            _context.SaveChanges();
        }

        public CmUser Login(LoginModel model) {
            CmUser user = null;
            try {
                int siteId = Util.ToInt(model.SiteId);
                /*
                if (model.Type == LoginModelType.본사) {
                    siteId = 1;
                }
                */
                user = (from c in _context.CmUser
                        where c.IsUse == true && c.SiteId == siteId && c.UserId == model.UserId
                        select c).FirstOrDefault();
                if (user != null && user.Passwd == model.Passwd) {
                    var tokenHandler = new JwtSecurityTokenHandler();
                    var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                    var tokenDescriptor = new SecurityTokenDescriptor {
                        Subject = new ClaimsIdentity(new Claim[]
                        {
                            new Claim(ClaimTypes.Name, user.UserId),
                            new Claim("UserId", user.UserId.ToString()),
                            new Claim("SiteId", user.SiteId.ToString()),
                            new Claim("UserGroupId", user.UserGroupId.ToString()),
                            new Claim("BusinessFieldId", user.BusinessFieldId.ToString()),
                            new Claim("Name", user.Name),
                            new Claim("IsAdmin", user.IsAdmin?"true":""),
                            new Claim("SiteName", user.CmSite.Name),
                        }),
                        Expires = DateTime.UtcNow.AddHours(10),
                        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                    };
                    var token = tokenHandler.CreateToken(tokenDescriptor);
                    user.AccessToken = tokenHandler.WriteToken(token);

                    _context.SaveChanges();

                } else {
                    return null;
                }
            } catch (Exception ex) {
                throw ex;
            }

            _context.Entry(user).State = EntityState.Deleted;
            return user;
        }

        public CmUser GetByUserId(string id, bool deleteCheck = false) {
            var item = _context.CmUser.AsNoTracking()
                            .Where(x => x.UserId == id).FirstOrDefault();
            if (deleteCheck && item.IsUse == false) return null;
            return item;
        }

        public IQueryable<CmUser> Gets(string id, int siteId) {
            var data = _context.CmUser.Where(x => x.UserId == id && x.SiteId == siteId);
            return data;
        }

        public void Save(CmUser data) {
            using (var transaction = _context.Database.BeginTransaction()) {
                if (data.CmFile != null && data.CmFile.IsUpload) {
                    data.CmFile.SiteId = data.SiteId;
                    var category = _context.CmFileCategory.First(x => x.Name == "user");
                    data.CmFile.FileCategory = category;
                    data.CmFile.CreatedDate = DateTime.Now;
                }

                var persist = _context.CmUser
                                .Where(x => x.UserId == data.UserId).FirstOrDefault();
                if (persist == null) {
                    var check = _context.CmUser.Where(x => x.UserId == data.UserId && x.IsUse == false).Count();
                    if (check > 0) {
                        throw new ServiceException("아이디가 중복됩니다.");
                    }

                    data.IsUse = true;
                    _context.CmUser.Add(data);
                    _context.SaveChanges();
                } else {
                    persist.SiteId = data.SiteId;
                    persist.UserGroupId = data.UserGroupId;
                    persist.UserId = data.UserId;
                    persist.Name = data.Name;
                    persist.OfficePhoneNo = data.OfficePhoneNo;
                    persist.EmploymentStatus = data.EmploymentStatus;
                    persist.EmploymentType = data.EmploymentType;
                    persist.CompanyId = data.CompanyId;
                    persist.DepartmentId = data.DepartmentId;
                    persist.PositionId = data.PositionId;
                    persist.BusinessFieldId = data.BusinessFieldId;
                    persist.MobilePhoneNo = data.MobilePhoneNo;
                    persist.EnterDate = data.EnterDate;
                    persist.RetireDate = data.RetireDate;
                    persist.Certificated = data.Certificated;
                    persist.IsScheduleUser = data.IsScheduleUser;
                    persist.IsSi = data.IsSi;
                    persist.IsMobile = data.IsMobile;
                    persist.IsAdmin = data.IsAdmin;
                    persist.Comment = data.Comment;
                    persist.Email = data.Email;
                    if (data.CmFile != null && data.CmFile.IsUpload) {
                        persist.CmFile = data.CmFile;
                    }
                    _context.SaveChanges();
                }

                if (data.CmFile != null && data.CmFile.IsUpload) {
                    // 카피
                    _storage.CopyEntity(data.CmFile.Path, persist.CmFile);
                }
                transaction.Commit();
            }
        }

        public void Create(CmUser data) {
            using (var transaction = _context.Database.BeginTransaction()) {
                if (data.CmFile != null && data.CmFile.IsUpload) {
                    data.CmFile.SiteId = data.SiteId;
                    var category = _context.CmFileCategory.First(x => x.Name == "user");
                    data.CmFile.FileCategory = category;
                    data.CmFile.CreatedDate = DateTime.Now;
                } else {
                    data.CmFile = null;
                }
                if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    var category = _context.CmFileCategory.First(x => x.Name == "user_sign");
                    data.CmFile1.SiteId = data.SiteId;
                    data.CmFile1.FileCategory = category;
                    data.CmFile1.CreatedDate = DateTime.Now;
                } else {
                    data.CmFile1 = null;
                }

                var persist = _context.CmUser.Where(x => x.UserId == data.UserId).FirstOrDefault();
                if (persist != null) {
                    throw new ServiceException("이미 동일 아이디가 존재합니다.");
                }

                data.IsUse = true;
                _context.CmUser.Add(data);
                _context.SaveChanges();

                if (data.CmFile != null && data.CmFile.IsUpload) {
                    // 카피
                    _storage.CopyEntity(data.CmFile.Path, data.CmFile);
                }
                if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    // 카피
                    _storage.CopyEntity(data.CmFile1.Path, data.CmFile1);
                }
                transaction.Commit();
            }
        }

        public void Edit(string id, int siteId, CmUser data) {
            using (var transaction = _context.Database.BeginTransaction()) {
                if (data.CmFile != null && data.CmFile.IsUpload) {
                    data.CmFile.SiteId = data.SiteId;
                    var category = _context.CmFileCategory.First(x => x.Name == "user");
                    data.CmFile.FileCategory = category;
                    data.CmFile.CreatedDate = DateTime.Now;
                }
                if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    var category = _context.CmFileCategory.First(x => x.Name == "user_sign");
                    data.CmFile1.FileCategory = category;
                    data.CmFile1.CreatedDate = DateTime.Now;
                }

                var persist = _context.CmUser.Where(x => x.UserId == id && x.SiteId == siteId).FirstOrDefault();
                if (persist == null) {
                    throw new ServiceException("정보를 찾을 수 없습니다.");
                }

                persist.UserGroupId = data.UserGroupId;
                persist.Name = data.Name;
                persist.Passwd = data.Passwd;
                persist.PasswordUpdatedDate = DateTime.Now;
                persist.OfficePhoneNo = data.OfficePhoneNo;
                persist.EmploymentStatus = data.EmploymentStatus;
                persist.EmploymentType = data.EmploymentType;
                persist.CompanyId = data.CompanyId;
                persist.DepartmentId = data.DepartmentId;
                persist.PositionId = data.PositionId;
                persist.BusinessFieldId = data.BusinessFieldId;
                persist.MobilePhoneNo = data.MobilePhoneNo;
                persist.EnterDate = data.EnterDate;
                persist.RetireDate = data.RetireDate;
                persist.Certificated = data.Certificated;
                persist.IsScheduleUser = data.IsScheduleUser;
                persist.IsSi = data.IsSi;
                persist.IsMobile = data.IsMobile;
                persist.IsAdmin = data.IsAdmin;
                persist.Comment = data.Comment;
                persist.Email = data.Email;
                if (data.CmFile != null && data.CmFile.IsUpload) {
                    persist.CmFile = data.CmFile;
                }
                if (data.CmFile1 != null) {
                    if (data.CmFile1.IsUpload) {
                        persist.CmFile1 = data.CmFile1;
                    }
                    if (data.CmFile1.IsDelete) {
                        persist.SignImageFileId = null;
                        persist.CmFile1 = null;
                    }
                }
                _context.SaveChanges();

                if (data.CmFile != null && data.CmFile.IsUpload) {
                    // 카피
                    _storage.CopyEntity(data.CmFile.Path, persist.CmFile);
                    if (persist.CmFile != null) {
                        // 파일 삭제시 진짜 DB삭제?
                    }
                }
                if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    //카피
                    _storage.CopyEntity(data.CmFile1.Path, persist.CmFile1);
                }

                transaction.Commit();
            }
        }

        public void ChangePassword(int siteId, string id, string password, bool bInit = false) {
            var data = _context.CmUser.First(x => x.UserId == id && x.SiteId == siteId);
            if (!bInit && data.Passwd == password) {
                //패스워드 초기화가 아닌 경우만 기존 패스워드 체크.
                throw new ServiceException("기존 패스워드와 동일한 패스워드를 사용할 수 없습니다.");
            }
            data.Passwd = password;
            data.PasswordUpdatedDate = DateTime.Now;
            _context.SaveChanges();
        }

        public void Delete(string id, int siteId) {
            var data = _context.CmUser.First(x => x.UserId == id && x.SiteId == siteId);
            _context.CmUser.Remove(data);
            _context.SaveChanges();
        }

        public IQueryable<CmUser> GetAll() {
            var query = _context.CmUser/*.AsNoTracking()*/
                .Include(x => x.CmUserGroup);
            return query;
        }

        public IQueryable<CmSite> GetCmSites() {
            var query = _context.CmSite.Where(x => x.IsUse == true);
            return query;
        }
    }
}