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;
using System.Transactions;

namespace FMSApp.Services {
    public class UserService {
        private readonly ILogger<UserService> _logger;
        private readonly FMSContext _context;

        private readonly AppSettings _appSettings;
        private readonly StorageHelper _storage;

        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(int siteid, string account, string password, string ip) {
            CmUser emp = null;
            try {
                emp = (from c in _context.CmUser.Include(x => x.CmSite)
                       where c.UserId == account
                       && c.CmSite.SiteId == siteid
                       && c.IsUse == true
                       && c.CmSite.IsUse == true
                       && c.IsMobile == true
                       select c).FirstOrDefault();
                if (emp != null && emp.Passwd == password) {
                    // authentication successful so generate jwt token

                    var businessFieldCommonId = _context.CmBusinessField.Where(
                        x => x.IsUse == true
                        && x.Name == "공통"
                    ).Select(x => x.BusinessFieldId).FirstOrDefault();

                    var tokenHandler = new JwtSecurityTokenHandler();
                    var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                    var tokenDescriptor = new SecurityTokenDescriptor {
                        Subject = new ClaimsIdentity(new Claim[]
                        {
                            new Claim(ClaimTypes.Name, emp.UserId),
                            new Claim("UserId", emp.UserId),
                            new Claim("SiteId", emp.SiteId.ToString()),
                            new Claim("SiteName", emp.CmSite.Name ?? ""),
                            new Claim("ApiSido", emp.CmSite.ApiSido ?? ""),
                            new Claim("ApiSigun", emp.CmSite.ApiSigun ?? ""),
                            new Claim("UserGroupId", emp.UserGroupId.ToString()),
                            new Claim("BusinessFieldId", emp.BusinessFieldId?.ToString() ?? businessFieldCommonId.ToString()),
                            new Claim("Name", emp.Name),
                            new Claim("IsAdmin", emp.IsAdmin ? "true" : ""),
                        }),
                        Expires = DateTime.UtcNow.AddDays(7),
                        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                    };
                    var token = tokenHandler.CreateToken(tokenDescriptor);
                    emp.AccessToken = tokenHandler.WriteToken(token);

                    _context.SaveChanges();

                } else {
                    if (emp.EmploymentStatus == EmploymentStatus.퇴직)
                        throw new ServiceException("로그인 할 수 없습니다.");
                    throw new ServiceException("아이디나 비밀번호가 잘못되었습니다.");
                }
            } catch (Exception ex) {
                throw ex;
            }

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

        public CmUser GetByUserId(int siteId, string userId, bool deleteCheck = false) {
            var item = _context.CmUser.Where(x => x.UserId == userId && x.SiteId == siteId).FirstOrDefault();
            if (deleteCheck && item.IsUse != true) return null;
            return item;
        }

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

        public void Save(CmUser data) {
            var persist = _context.CmUser
                             .Where(x => x.UserId == data.UserId).FirstOrDefault();

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

                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) {
                    data.CmFile1.SiteId = data.SiteId;
                    var category = _context.CmFileCategory.First(x => x.Name == "user_sign");
                    data.CmFile1.FileCategory = category;
                    data.CmFile1.CreatedDate = DateTime.Now;
                } else {
                    data.CmFile1 = null;
                }

                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);
                }

            } else {

                if (data.CmFile != null && data.CmFile.IsDelete) {
                    persist.CmFile = null;
                    persist.FileId = null;
                } else if (data.CmFile != null && data.CmFile.IsUpload) {
                    var category = _context.CmFileCategory.First(x => x.Name == "user");
                    persist.CmFile = new CmFile {
                        SiteId = data.SiteId,
                        FileCategory = category,
                        CreatedDate = DateTime.Now,
                        Name = data.CmFile.Name,
                        FileSize = data.CmFile.FileSize,
                        ContentType = data.CmFile.ContentType,
                    };
                }
                if (data.CmFile1 != null && data.CmFile1.IsDelete) {
                    persist.CmFile1 = null;
                    persist.SignImageFileId = null;
                } else if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    var category = _context.CmFileCategory.First(x => x.Name == "user_sign");
                    persist.CmFile1 = new CmFile {
                        SiteId = data.SiteId,
                        FileCategory = category,
                        CreatedDate = DateTime.Now,
                        Name = data.CmFile1.Name,
                        FileSize = data.CmFile1.FileSize,
                        ContentType = data.CmFile1.ContentType,
                    };
                }


                if (!String.IsNullOrEmpty(data.Passwd)) persist.Passwd = data.Passwd;

                persist.SiteId = data.SiteId;
                persist.UserId = data.UserId;
                persist.Name = data.Name;
                persist.CompanyId = data.CompanyId;
                persist.DepartmentId = data.DepartmentId;
                persist.PositionId = data.PositionId;
                persist.BusinessFieldId = data.BusinessFieldId;
                persist.UserGroupId = data.UserGroupId;
                persist.EmploymentStatus = data.EmploymentStatus;
                persist.EmploymentType = data.EmploymentType;
                persist.MobilePhoneNo = data.MobilePhoneNo;
                persist.OfficePhoneNo = data.OfficePhoneNo;
                persist.Comment = data.Comment;
                persist.Email = data.Email;
                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.IsUse = data.IsUse;
                _context.CmUser.Update(persist);
                _context.SaveChanges();

                if (data.CmFile != null && data.CmFile.IsUpload) {
                    _storage.CopyEntity(data.CmFile.Path, persist.CmFile);
                    if (persist.CmFile != null) {
                    }
                }
                if (data.CmFile1 != null && data.CmFile1.IsUpload) {
                    _storage.CopyEntity(data.CmFile1.Path, persist.CmFile1);
                    if (persist.CmFile1 != null) {
                    }
                }

            }
        }

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

        public IQueryable<CmUser> GetAll() {
            var query = _context.CmUser;
            return query;
        }

        public void UpdatePushToken(CmUser data) {
            using (var tran = new TransactionScope()) {
                var users = _context.CmUser.Where(
                    x => x.Devicetoken == data.Devicetoken
                ).ToList();
                foreach (var usr in users) {
                    usr.Devicetoken = "";
                    _context.CmUser.Update(usr);
                }
                _context.SaveChanges();

                var persist = _context.CmUser.FirstOrDefault(x => x.SiteId == data.SiteId && x.UserId == data.UserId);
                if (persist == null) {
                    throw new ServiceException("푸시알림 등록실패 : 사용자를 찾을수 없습니다.");
                }
                if (string.IsNullOrEmpty(data.Devicetoken)) {
                    throw new ServiceException("푸시알림 등록실패 : 장치정보가 없습니다.");
                }

                persist.Devicetoken = data.Devicetoken;
                _context.CmUser.Update(persist);
                _context.SaveChanges();

                tran.Complete();
            }
        }
    }
}