using Dapper;
using Dapper.Contrib.Extensions;
using MailKit.Security;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using MimeKit;
using Newtonsoft.Json;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using static ad_login.Controllers.ApiController;
using static DbTableClass;

namespace ad_login.Controllers
{
    [EnableCors("any")]
    [Route("adApi")]
  
    public class AdApiController : ControllerBase
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IWebHostEnvironment _hostingEnvironment;
        private readonly PasswordManagementService _passwordManagement;
        private readonly string _ldapServer;
        private readonly string _domain;
        private readonly string _baseDn;

        DbConn dbConn = new DbConn();
        SqlConnection conn = new SqlConnection(GlobalClass.appsettings("ConnectionStrings:SQLConnectionString"));


        public AdApiController(IHttpContextAccessor httpContextAccessor, IWebHostEnvironment webHostEnvironment, PasswordManagementService passwordManagement, IOptions<LdapSettings> ldapSettings)
        {
            this._httpContextAccessor = httpContextAccessor;
            this._hostingEnvironment = webHostEnvironment;
            this._passwordManagement = passwordManagement;
            this._ldapServer = ldapSettings.Value.Server;
            this._domain = ldapSettings.Value.Domain;
            this._baseDn = ldapSettings.Value.BaseDn;
        }

        [EnableCors("any")]
        [Route("resetPassword")]
        [SupportedOSPlatform("windows")]
        public ActionResult ResetPassword(IFormCollection obj)
        {
            Result ret = new Result();

            string new_password = obj["new_password"].ToString();
            string token = obj["token"].ToString();
            
            if (new_password == "")
            {
                ret.message = "new_password is null";
                ret.err_code = "1003";
                ret.ret = "no";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }

            mailToken objMailToken = conn.QueryFirstOrDefault<mailToken>("select * from mailToken where mailToken_value=@mailToken_value and mailToken_useed='N' and mailToken_endDate>=getdate()", new { mailToken_value = token });

            if (objMailToken == null)
            {
                ret.ret = "no";
                ret.err_code = "1001";
                ret.message = "token無效或過期，或是已經變更密碼過了，請重新申請忘記密碼步驟";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }


            try {
                DirectoryEntry entry = new DirectoryEntry($"LDAP://{_ldapServer}/{_baseDn}", GlobalClass.appsettings("LdapSettings:User"), GlobalClass.appsettings("LdapSettings:Password")); // 使用 LDAP 伺服器和基礎 DN 建立 DirectoryEntry 物件。
                DirectorySearcher mySearcher = new DirectorySearcher(entry);
                mySearcher.Filter = ("(&(objectClass=user)(sAMAccountName=" + objMailToken.mailToken_account + "))");

                SearchResult searchResult = mySearcher.FindOne();
                DirectoryEntry userEntry = searchResult.GetDirectoryEntry();
                userEntry.Invoke("SetPassword", new object[] { new_password });

                ret.ret = "yes";

                objMailToken.mailToken_useed = "Y";

                conn.Update<mailToken>(objMailToken);
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");

            } catch (Exception ex) {
                ret.ret = "no";
                ret.err_code = "1002";
                ret.message = "修改密碼失敗，請聯絡系統管理員 原因為" + ex.Message;
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }



            return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
        }

        [EnableCors("any")]
        [Route("forgetPassword")]
        [SupportedOSPlatform("windows")]
        public async Task<ActionResult> ForgetPasswordAsync(IFormCollection obj)
        {
            Result ret = new Result();
            string userEmail = obj["userEmail"].ToString();
            string userAccount = obj["userAccount"].ToString();

            
            if (userAccount == "")
            {
                ret.message = "userAccount is null";
                ret.err_code = "1003";
                ret.ret = "no";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }

            if (userEmail == "")
            {
                ret.message = "userEmail is null";
                ret.err_code = "1004";
                ret.ret = "no";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }

            using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, _domain, _baseDn, GlobalClass.appsettings("LdapSettings:User"), GlobalClass.appsettings("LdapSettings:Password")))
            {
                UserPrincipal user = UserPrincipal.FindByIdentity(pc, userAccount);
                if (user != null)
                {

                    if (user.Enabled == false)
                    {
                        ret.message = "此帳號已被停用";
                        ret.err_code = "1007";
                        ret.ret = "no";
                        return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
                    }

                    string emailaddress = user.EmailAddress ?? "";
                    
                    if (emailaddress != userEmail)
                    {
                        ret.message = "帳號與Email不符，不是公司發給此帳號的Email";
                        ret.err_code = "1005";
                        ret.ret = "no";
                        return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
                    }

                    mailToken objMailToken = new mailToken();
                    objMailToken.mailToken_value = GlobalClass.CreateRandomCode(24);
                    objMailToken.mailToken_mail = userEmail;
                    objMailToken.mailToken_endDate = DateTime.Now.AddHours(12);
                    objMailToken.mailToken_account = userAccount;

                    conn.Insert<mailToken>(objMailToken);

                    string htmlBody = "";

                    htmlBody += "<H2>重設登入PC或公槽密碼確認信</H2>";
                    htmlBody += "<br/>如果您有重設登入PC或公槽密碼需求的話，請點下面連結開始網頁重設密碼。如果沒有請忽略此信!";
                    htmlBody += "<br/><br/>重設密碼連結：<a href='" + "https://ad-pass.bremen.com.tw/Home/reset?token=" + objMailToken.mailToken_value + "' target='_blank'>" + "https://ad-pass.bremen.com.tw/Home/reset?token=" + objMailToken.mailToken_value +  "</a>";
                    htmlBody += "<br/><br/>PS:此連結於" + objMailToken.mailToken_endDate.ToString("yyyy/MM/dd HH:mm") + "前有效!";

                    MailRequest mailRequest = new MailRequest();
                    mailRequest.ToEmail = userEmail;

                    mailRequest.Body = htmlBody;

                    mailRequest.Subject = "重設登入PC或公槽密碼確認信";

                    await SendEmailAsync(mailRequest);

                    ret.ret = "yes";

                    return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
                }
                else
                {
                    ret.message = "User not found";
                    ret.err_code = "1006";
                    ret.ret = "no";
                    return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
                }
            }
  
            return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
        }

            [EnableCors("any")]
        [Route("aduserList")]
        [SupportedOSPlatform("windows")]
        public ActionResult AduserList(IFormCollection obj) {

            Result ret = new Result();

            string query_token = obj["query_token"].ToString();

            if (query_token == "") {
                ret.message = "query_token is null";
                ret.err_code = "1001";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }

            if (query_token != GlobalClass.appsettings("LdapSettings:query_token"))
            {
                ret.message = "query_token is wrong";
                ret.err_code = "1002";
                return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
            }

            List<String> expiringUsers = [];

            DirectoryEntry entry = new DirectoryEntry($"LDAP://{_ldapServer}/{_baseDn}", GlobalClass.appsettings("LdapSettings:User"), GlobalClass.appsettings("LdapSettings:Password")); // 使用 LDAP 伺服器和基礎 DN 建立 DirectoryEntry 物件。
            DirectorySearcher mySearcher = new DirectorySearcher(entry);
            mySearcher.Filter = "(&(objectCategory=person)(objectClass=user))"; // 篩選有「上次密碼設定時間」、「Mail」的「使用者」。
              // 電子郵件

            foreach (SearchResult result in mySearcher.FindAll())
            {
                string userSAMAccountName = result.Properties["sAMAccountName"][0].ToString() ?? string.Empty; // AD 帳號
                string userDisplayName = "";
                string userMail = "";
                if (result.Properties["displayName"].Count > 0)
                    userDisplayName = result.Properties["displayName"][0].ToString();
                else
                    userDisplayName = userSAMAccountName;       // 顯示名稱

                if (result.Properties["mail"].Count > 0) {
                   userMail = result.Properties["mail"][0].ToString() ?? string.Empty;
                }

                int flags = (int)result.Properties["userAccountControl"][0];
                string expiringUsersInfo = $"{userSAMAccountName};{userDisplayName};{userMail}";

                adUser adUser = new adUser
                {
                    userAccount = userSAMAccountName,
                    userDisplayName = userDisplayName,
                    userMail = userMail
                };

                if (!Convert.ToBoolean(flags & 0x0002) && (userMail != "")) {
                    ret.data.Add(adUser); // 如果帳號沒有被停用，且有電子郵件，就加入結果列表。
                }
            }

            ret.ret = "yes";

            return Content(JsonConvert.SerializeObject(ret), "application/json;charset=utf-8");
        }

        public async Task SendEmailAsync(MailRequest mailRequest)
        {
            var email = new MimeMessage();
            email.Sender = MailboxAddress.Parse(GlobalClass.appsettings("MailServer:smtp_username"));
            email.To.Add(MailboxAddress.Parse(GlobalClass.appsettings("MailServer:smtp_username")));

            foreach (string item in mailRequest.ToEmail.Split(','))
            {
                email.Bcc.Add(MailboxAddress.Parse(item));
            }

            email.Subject = "重設登入PC或公槽密碼確認信";

            var builder = new BodyBuilder();

            if (mailRequest.attach != null)
            {
                builder.Attachments.Add(mailRequest.attachName, mailRequest.attach);
            }

            builder.HtmlBody = mailRequest.Body;
            email.Body = builder.ToMessageBody();

            using var smtp = new MailKit.Net.Smtp.SmtpClient();
            smtp.Connect(GlobalClass.appsettings("MailServer:smtp_host"), int.Parse(GlobalClass.appsettings("MailServer:smtp_port")), SecureSocketOptions.StartTls);
            smtp.Authenticate(GlobalClass.appsettings("MailServer:smtp_username"), GlobalClass.appsettings("MailServer:smtp_password"));
            await smtp.SendAsync(email);
            smtp.Dispose();

        }

        public class Result
        {
            public string ret = "no";
            public string err_code = "0000";
            public string message = "";
            public List<adUser> data = new List<adUser>();
        }

        public class adUser
        {
            public string userAccount { get; set; } = string.Empty; // AD 帳號
            public string userDisplayName { get; set; } = string.Empty; // 顯示名稱
            public string userMail { get; set; } = string.Empty; // 電子郵件
        }

    }
}
