using System;
using System.Linq;
using System.Threading.Tasks;

using FMSAdmin.Data;
using FMSAdmin.Models;
using FMSAdmin.Entities;
using FMSAdmin.Services;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using FMSAdmin.Helpers;
using System.Data;
using System.IO;
using OfficeOpenXml;
using System.Security.Cryptography;
using System.Text;
using System.Net.Mail;
using System.Net;

namespace FMSAdmin.Controllers {
    [Authorize]
    [ApiController]
    [ApiVersion("1")]
    [Route("api/users")]
    public class UserController : Controller {
        private readonly ILogger<UserController> _logger;
        private readonly FMSContext _context;
        private readonly UserService _service;
        private readonly StorageHelper _storage;

        public UserController(
            ILogger<UserController> logger,
            FMSContext context,
            UserService service,
            StorageHelper storage
            ) {
            _logger = logger;
            _context = context;
            _service = service;
            _storage = storage;
        }

        /// <summary>
        /// 로그인
        /// </summary>
        [AllowAnonymous]
        [HttpPost("[action]")]
        public IActionResult Login([FromBody] LoginModel model) {
            _logger.LogInformation(model.Dump());

            var user = _service.Login(model);
            if (user == null)
                return BadRequest("아이디나 비밀번호가 잘못되었습니다.");
            if (user.EmploymentStatus == EmploymentStatus.퇴직)
                return BadRequest("로그인 할 수 없습니다.");
            return Ok(new {
                user.SiteId,
                user.UserId,
                user.UserGroupId,
                UserGroup = new {
                    user.UserGroupId,
                    user.Name,
                },
                user.Name,
                user.OfficePhoneNo,
                user.Email,
                user.PositionId,
                Position = new {
                    user.CmPosition?.Name,
                },
                SiteName = user.CmSite?.Name,
                user.RefreshToken,
                user.AccessToken,
                user.IsAdmin,
                user.PasswordUpdatedDate,
                user.IsSysop,
            });
        }

        /// <summary>
        /// 패스워드 변경
        /// </summary>
        [AllowAnonymous]
        [HttpPost("[action]/{siteId}/{id}")]
        public IActionResult ChangePassword(int siteId, string id, [FromForm] string password, [FromForm] string auth) {
            _logger.LogInformation("password:" + password);
            _logger.LogInformation("auth:" + auth);
            // auth 체크는 나중에..
            _service.ChangePassword(siteId, id, password);
            return Ok(id);
        }

        /// <summary>
        /// 목록
        /// </summary>
        [HttpGet]
        public IActionResult List([FromQuery] PagingRequest req, [FromQuery] string str, int? type) {
            _logger.LogInformation(req.Dump());

            var query = _FilterAndSort(req, str);

            if (type != null) query = query.Where(x => x.EmploymentStatus == EmploymentStatus.재직);

            var list = _ListSelect(query);

            var paging = PagingList.Pagination(list, req.page, req.limit);

            return Ok(paging);
        }

        /// <summary>
        /// 엑셀파일 익스포트
        /// </summary>
        [HttpGet("excel")]
        public IActionResult ExportExcel([FromQuery] PagingRequest req, string filename) {
            req.page = 1;
            req.limit = 0;
            var query = _FilterAndSort(req, "");

            var list = _ListSelect(query);

            var stream = new MemoryStream();
            using (var package = new ExcelPackage(stream)) {
                var workSheet = package.Workbook.Worksheets.Add("Sheet1");

                workSheet.Cells.LoadFromCollection(list, true);

                if (req.columns != null && req.columns.Length > 0) {
                    workSheet.AddStyle(req.columns, req.sort?.order?.ToLower() == "asc", filename);
                }
                package.Save();
            }
            stream.Position = 0;
            string excelName = $"excel-{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.xlsx";
            return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", excelName);
        }

        /// <summary>
        /// 상세조회
        /// </summary>
        [HttpGet("{id}")]
        public IActionResult GetByUserId(string id) {
            var emp = _service.GetByUserId(id);

            if (emp == null)
                return NotFound();

            return Ok(new {
                emp.SiteId,
                emp.UserId,
                emp.UserGroupId,
                emp.Name,
                emp.OfficePhoneNo,
                emp.Email,
                //UserGroupName = emp.CmUserGroup.Name,
                emp.AccessToken,
            });
        }

        /// <summary>
        /// 조회
        /// </summary>
        /// <param name="siteId"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("{siteId}/{id}")]
        public IActionResult Get(int siteId, string id) {
            var data = _service.Gets(id, siteId);
            if (data.Count() == 0) {
                return NotFound("사용자정보를 찾을 수 없습니다.");
            }
            var item = data.Select(x => new {
                x.UserGroupId,
                CmUserGroup = new {
                    x.CmUserGroup.Name,
                },
                x.SiteId,
                CmSite = new {
                    x.CmSite.Name,
                },
                x.UserId,
                x.Name,
                x.Passwd,
                x.OfficePhoneNo,
                x.Email,
                x.EmploymentStatus,
                EmploymentStatusName = x.EmploymentStatus.ToString(),
                x.EmploymentType,
                EmploymentTypeName = x.EmploymentType.ToString(),
                x.CompanyId,
                CmCompany = new {
                    x.CmCompany.Name,
                },
                x.DepartmentId,
                CmDepartment = new {
                    x.CmDepartment.Name,
                },
                x.PositionId,
                CmPosition = new {
                    x.CmPosition.Name,
                },
                x.BusinessFieldId,
                CmBusinessField = new {
                    x.CmBusinessField.Name,
                },
                x.MobilePhoneNo,
                x.EnterDate,
                x.RetireDate,
                x.Certificated,
                x.IsScheduleUser,
                x.IsSi,
                x.IsMobile,
                x.Comment,
                x.IsUse,
                x.IsAdmin,
                CmFile = _storage.ParseFile(x.CmFile),
                CmFile1 = _storage.ParseFile(x.CmFile1),
            });

            return Ok(item.First());
        }

        /// <summary>
        /// 수정
        /// </summary>
        [HttpPost("{siteId}/{id}")]
        public IActionResult Edit(int siteId, string id, [FromBody] CmUser emp) {
            _logger.LogInformation(emp.Dump());
            /*
            var currentUserId = int.Parse(User.Identity.Name);
            if (id != currentUserId)
                return Forbid();
            if (!User.IsInRole(Role.Admin))
                return Forbid();
            */

            if (id != emp.UserId) {
                return NotFound();
            }

            //ModelState.Remove("Password"); // 이 수정에서는 비밀번호 제외
            if (ModelState.IsValid) {
                try {
                    _service.Edit(id, siteId, emp);
                } catch (ServiceException ex) {
                    return BadRequest(ex.Message);
                }
            } else {
                foreach (var ms in ModelState.ToArray()) {
                    _logger.LogInformation(ms.Key);
                }
                return BadRequest(ModelState);
            }

            return Ok();
        }

        /// <summary>
        /// 등록
        /// </summary>
        [HttpPut]
        public IActionResult Create([FromBody] CmUser emp) {
            //            if (!User.IsInRole("Admin"))
            //                return Forbid();

            if (ModelState.IsValid) {
                try {
                    _service.Create(emp);
                } catch (ServiceException ex) {
                    return BadRequest(ex.Message);
                }
            } else {
                return BadRequest(ModelState);
            }
            return Ok();
        }

        /// <summary>
        /// 삭제
        /// </summary>
        [HttpDelete("{siteId}/{id}")]
        public IActionResult Delete(int siteId, string id) {
            _service.Delete(id, siteId);

            return Ok();
        }

        // Role 제한 테스트
        [AllowAnonymous]
        [HttpGet("auth")]
        public IActionResult Auth() {

            return Ok();
        }

        // 검색 & 정렬 공통
        private IQueryable<CmUser> _FilterAndSort(PagingRequest req, string str) {
            var query = _service.GetAll();

            // 기본 Entity 검색
            query = query.Filter(req.conditions);

            // 추가 검색 예제
            /*
            if (req.conditions != null) {
                foreach (var cond in req.conditions) {
                    switch ($"{cond.field}_{cond.op}") {
                        case "fffff_eq":
                            query = query.Where(x => x.Name == cond.value);
                            break;
                    }
                }
            }
            */
            //query = query.Where(x => x.IsUse == true);
            //아이콘트롤스 시스템 관리자 HIDDEN
            query = query.Where(x => x.IsSysop == false);

            if (req.siteId != null) {
                query = query.Where(x => x.SiteId == Util.ToInt(req.siteId));
            }
            if (req.siteIds != null && req.siteIds.Length > 0) {
                query = query.Where(x => req.siteIds.Contains(x.SiteId));
            }

            // 업무분야 필드가 있는경우만 추가
            if (req.businessFieldId != null) {
                query = query.Where(x => x.SiteId == req.siteId);
                query = query.Where(x => x.BusinessFieldId == req.businessFieldId || x.CmBusinessField.Name == "공통");
            }

            if (str != null) {
                query = query.Where(x => x.UserId.Contains(str) || x.Name.Contains(str));
            }

            // 추가 정렬 예제
            /*
            if (req.sort != null) {
                switch ($"{req.sort.field}_{req.sort.order}") {
                    case "userId_asc":
                        query = query.OrderBy(x => x.UserId);
                        break;
                    case "userId_desc":
                        query = query.OrderByDescending(x => x.UserId);
                        break;
                }
            }
            */

            // 고정정렬 (사옥 준공순)
            var sortQuery = query.OrderBy(x => x.CmSite.SortOrderNo);
            sortQuery = sortQuery.ThenByDescending(x => x.CmPosition.Position);

            // 추가 정렬
            sortQuery = sortQuery.ThenSort(req.sort);

            return sortQuery;
        }

        // 셀렉트 공통
        private dynamic _ListSelect(IQueryable<CmUser> query) {
            var list = query.Select(x => new {
                x.SiteId,
                SiteName = x.CmSite.Name,
                x.UserId,
                x.UserGroupId,
                x.Name,
                CmUserGroup = new {
                    x.CmUserGroup.UserGroupId,
                    x.CmUserGroup.Name
                },
                UserGroupName = x.CmUserGroup.Name,
                x.EmploymentStatus,
                EmploymentStatusName = x.EmploymentStatus.ToString(),
                x.OfficePhoneNo,
                x.MobilePhoneNo,
                x.Email,
                x.AccessToken,
                CmCompanyNm = new {
                    x.CmCompany.Name
                },
                CompanyName = x.CmCompany.Name,
                CmDepartment = new {
                    x.CmDepartment.Name
                },
                DepartmentName = x.CmDepartment.Name,
                Dept = x.CmCompany.Name + '/' + x.CmPosition.Name,
                PositionName = x.CmPosition.Name,
                BusinessFieldName = x.CmBusinessField.Name,

                x.EmploymentType,
                EmploymentTypeName = x.EmploymentType.ToString(),
                CmFile = _storage.ParseFile(x.CmFile)
            });

            return list;
        }

        /// <summary>
        /// 로그인 - 사이트 ID 조회
        /// </summary>
        [AllowAnonymous]
        [HttpGet("GetSite")]
        public IActionResult GetSite() {
            var site = _service.GetCmSites();
            if (site == null)
                return BadRequest("사이트 정보를 가져오지 못했습니다.");

            var list = site.Select(x => new {
                Id = x.SiteId,
                x.Name,
            });

            return Ok(list);
        }

        [AllowAnonymous]
        [HttpPost("[action]/{reqid}/{email}")]
        public IActionResult SendEmailAuthCode(string email, string reqid) {
            // 요청번호 reqid 무시. 안쓰고있음

            if (string.IsNullOrEmpty(email)) {
                return BadRequest("이메일을 입력해주세요.");
            }
            email = email.Trim();
            var query = _service.GetAll().Where(x => x.Email == email);
            if (query.Count() == 0) {
                return BadRequest("이메일과 일치하는 회원이 없습니다.");
            }
            if (query.Count() > 1) {
                return BadRequest("동일한 이메일이 여러개 존재합니다.");
            }
            string authCode = "" + new Random().Next(10000, 99999);
            _logger.LogInformation("authCode:" + authCode);

            var user = query.FirstOrDefault();

            // 이메일 발송
            using (var message = new MailMessage()) {
                message.To.Add(new MailAddress(email, user.Name));
                message.From = new MailAddress("hdc.icontrols.rnd1@gmail.com", "FMS");
                message.Subject = "FMS 비밀번호 찾기 인증번호";
                message.Body = $"<div style='width:800px; padding:30px; text-align:center;font-size:20pt;'>{user.Name}님의 아이디는 {user.UserId.Substring(0, user.UserId.Length - 2)}** 입니다.<br/><br/><div style='font-size:15pt'>보안을 위해 아래 인증번호를 입력하시고,<br/>비밀번호를 새로 설정해 주시기 바랍니다.</div><br/><strong>인증번호 : {authCode}</strong></div>";
                message.IsBodyHtml = true;
                var smtp = new SmtpClient {
                    Host = "smtp.gmail.com",
                    Port = 587,
                    EnableSsl = true,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    Credentials = new NetworkCredential("hdc.icontrols.rnd1@gmail.com", "pwrd12#$"),
                    Timeout = 20000
                };
                smtp.Send(message);
            }

            // 검증용 코드 출력
            string code = null;
            using (SHA256 mySHA256 = SHA256.Create()) {
                code = Convert.ToBase64String(mySHA256.ComputeHash(Encoding.UTF8.GetBytes(email + authCode))).Replace("+", ".").Replace("/", "_").Replace("=", "-");
            }
            return Ok(code);
        }

        [AllowAnonymous]
        [HttpPost("[action]/{authCode}/{code}/{email}")]
        public IActionResult ValidAuthCode(string authCode, string code, string email) {
            string check = "";
            if (string.IsNullOrEmpty(email)) {
                return BadRequest("이메일을 입력해주세요.");
            }
            if (string.IsNullOrEmpty(authCode)) {
                return BadRequest("인증번호를 입력해주세요.");
            }
            var query = _service.GetAll().Where(x => x.Email == email);
            if (query.Count() == 0) {
                return BadRequest("이메일과 일치하는 회원이 없습니다.");
            }
            if (query.Count() > 1) {
                return BadRequest("동일한 이메일이 여러개 존재합니다.");
            }
            var member = query.FirstOrDefault();

            // 검증용 코드로 검증
            authCode = authCode.Trim();
            using (SHA256 mySHA256 = SHA256.Create()) {
                check = Convert.ToBase64String(mySHA256.ComputeHash(Encoding.UTF8.GetBytes(email + authCode))).Replace("+", ".").Replace("/", "_").Replace("=", "-");
            }
            if (code != check) {
                return BadRequest("인증번호가 일치하지 않습니다.");
            }

            // 검증완료 비밀번호 초기화
            _service.ChangePassword(member.SiteId, member.UserId, member.UserId, true);
            return Ok(new {
                SiteId = member.SiteId,
                UserId = member.UserId,
            });
        }
    }
}