using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PSTW_CentralSystem.DBContext;
using PSTW_CentralSystem.Models;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace PSTW_CentralSystem.Controllers.API
{
    [ApiController]
    [Route("[controller]")]

    public class IdentityAPI : Controller
    {
        private readonly ILogger<IdentityAPI> _logger;
        private readonly CentralSystemContext _identityDbContext;
        private readonly UserManager<UserModel> _userManager;
        private readonly SignInManager<UserModel> _signInManager;
        // Communication Key for API. Not API authentication key
        private readonly string _commKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF4NW42anlkNlpTYzZNSE1Zem9qaApUbldpYTIra2pud2ZNbVhpSWlyK0RadjM2cEVGMGhRUWFLaWpaMWtyMGNiT25Ha2d2QnNwTzNiYkFua0E3SWwzCk4zM3NNYWdQV0JOQzZyVm1jT04zNEhDSWJCM0hvQXFYQUtkSHFUOGZneklMRzFhRzdxK2h4RDZhZzZsemhTMnEKdDA1bHdNc0hONWpOdmVNNnFWalRnTVB4aEFOMUhnUTkrd1lRWFh5bnZYYUo5OUNySHBqS21WdUt2VUh6WXdlRwp6SnBtYXZOclc4bE9oM1lMeVNuUVU5bjRrdURubGc1OWNHeUtKbzJ2YUxZbll4MkR1ZDNabzBXMHRMWGd0dlQyCjVXdVFsY0NVbldvaVpBV1JBTGI3anRpcTF0MGY5eVBiV2gxYXpMMjFoL3QvckJUMXNCL2FQd2kzRCt3MnBUR00KeVFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==";


        public IdentityAPI(ILogger<IdentityAPI> logger, CentralSystemContext authDbContext, UserManager<UserModel> userManager, SignInManager<UserModel> signInManager)
        {
            _logger = logger;
            _identityDbContext = authDbContext;
            _userManager = userManager;
            _signInManager = signInManager;
        }

        #region User
        [HttpPost("GetUserInformation")] 
        public async Task<IActionResult> GetUserInformation()
        {
            try
            {
                var user = await _userManager.GetUserAsync(User);
                var userRole = await _userManager.GetRolesAsync(user!);
                if (user == null)
                {
                    return NotFound(new { message = $"Unable to load user with ID '{_userManager.GetUserId(User)}'." });
                }

                var storeId = await _identityDbContext.InventoryMasters.Where(s => s.UserId == user.Id).Select(s => s.StoreId).FirstOrDefaultAsync();

                var userInfo = await _identityDbContext.Users.Include(u => u.Department)
                    .Include(u => u.Store)
                    .Select(u => new
                {
                    id = u.Id,
                    email = u.NormalizedEmail,
                    company = u.Department!.Company!.CompanyName,
                    department = u.Department,
                    role = userRole,
                    store = storeId !=  0 ? storeId : (int?)null
                    }).Where(u => u.id == user.Id).FirstOrDefaultAsync();

                if (userInfo == null)
                {
                    return NotFound(new { message = "User not found" });
                }

                return Ok(new { UserInfo = userInfo });
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"An error occurred: {ex.Message}");
            }
        }
        #endregion User

        #region LDAP Login
        [HttpPost("LdapLogin")]
        public async Task<IActionResult> LdapLogin([FromBody] LdapLoginCredential ldapLoginInfo)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            byte[] noFormatString = Convert.FromBase64String(_commKey);
            string initUrlKey = Encoding.UTF8.GetString(noFormatString);

            string jsonData = JsonSerializer.Serialize(ldapLoginInfo);

            RSA rsaBase = RSA.Create();
            rsaBase.ImportFromPem(initUrlKey.ToCharArray());
            byte[] rsaData = rsaBase.Encrypt(Encoding.UTF8.GetBytes(jsonData), RSAEncryptionPadding.Pkcs1);
            string rsaDataBase64 = Convert.ToBase64String(rsaData);

            string ldapUrl = "http://192.168.11.231/api/ldap/";
            string ldapUrlResult = "";

            using (HttpClient httpClient = new HttpClient())
            {
                try
                {
                    StringContent rsaDataB64HttpContent = new(rsaDataBase64, Encoding.UTF8);
                    HttpResponseMessage ldapUrlResponse = await httpClient.PostAsync(ldapUrl, rsaDataB64HttpContent);
                    ldapUrlResponse.EnsureSuccessStatusCode();
                    if (ldapUrlResponse.IsSuccessStatusCode)
                    {
                        ldapUrlResult = await ldapUrlResponse.Content.ReadAsStringAsync();
                    }
                }
                catch (Exception e)
                {
                    return BadRequest(new { Message = $"Message: {e.Message}\nException Caught!" });
                }
            }

            userLdapInfo userLdapInfo = JsonSerializer.Deserialize<userLdapInfo>(ldapUrlResult)!;

            if (!userLdapInfo.Authenticated)
            {
                return BadRequest(new { Message = $"Login Failed. {userLdapInfo.Response}" });
            }

            userInfo userInfo = userLdapInfo.UserInfo!;

            UserModel ldapuser = new UserModel()
            {
                FullName = userInfo!.Username,
                UserName = userInfo.Email,
                NormalizedUserName = userInfo.Email.ToUpper(),
                Email = userInfo.Email,
                NormalizedEmail = userInfo.Email.ToUpper(),
                EmailConfirmed = true,
                PhoneNumberConfirmed = false,
                TwoFactorEnabled = false,
                LockoutEnabled = false,
                AccessFailedCount = 0,
            };
            var existUser = await doUserExists(ldapuser.Email);
            if (existUser == null)
            {
                await _userManager.CreateAsync(ldapuser);
                await _signInManager.SignInAsync(ldapuser, false);
                return Ok(new { RedirectUrl = Url.Action("ComDeptAssignment", "Identity") });

            };

            await _signInManager.SignInAsync(existUser, false);

            if (existUser.UserInfoStatus == null || existUser.UserInfoStatus == 0)
            {
                return Ok(new { RedirectUrl = Url.Action("ComDeptAssignment", "Identity") });
            }

            return Ok(new { RedirectUrl = Url.Action("Index", "Home") });

        }
        #endregion LDAP Login

        #region Company

        [HttpPost("CompanyDepartmentList")]
        public async Task<IActionResult> CompanyDepartmentList()
        {
            var companyList = await _identityDbContext.Companies
                .Include(c => c.Departments)
                .Select(c => new {
                    c.CompanyId,
                    c.CompanyName,
                    Departments = c.Departments
                    .OrderBy(d => d.DepartmentId)
                    .Select(d => new { d.DepartmentId, d.DepartmentName })
                })
                .ToListAsync();
            return Json(companyList);
        }

        #endregion Company

        #region Department

        [HttpPost("DepartmentCompanyList")]
        public async Task<IActionResult> DepartmentCompanyList()
        {
            var itemDepartment = await _identityDbContext.Departments
            .Include(d => d.Company) // Include the related Company entity
            .Select(d => new
            {
                d.DepartmentId,
                d.DepartmentName,
                d.CompanyId,
                d.Company.CompanyName,
            })
            .ToListAsync();

            //return Json(await GetDepartmentWithCompanyList());
            return Json(itemDepartment);
        }

        [HttpPost("UserComptDeptAssignment/{id}")]
        public async Task<IActionResult> UserComptDeptAssignment([FromBody] UserDeptAssignment userDeptAssignment, int id)
        {
            var deptId = userDeptAssignment.departmentId;
            if (deptId <= 0)
            {
                return BadRequest(new { message = "Invalid department ID provided." });
            }
            var user = await _userManager.FindByIdAsync(id.ToString());
            if(user == null) 
            { 
                return NotFound( new { message = $"Unable to load user with ID '{id}'." }); 
            }
            user.UserInfoStatus = 0;
            user.departmentId = deptId;
            await _userManager.UpdateAsync(user);
            return Ok( new { message = "User updated successfully", RedirectUrl = Url.Action("Index", "Home") });
        }

        #endregion Department

        public async Task<UserModel?> doUserExists(string username)
        {
            var user = await _userManager.FindByNameAsync(username);
            return user != null ? user : null;
        }

        public class LdapLoginCredential
        {
            public required string username { get; set; }
            public required string password { get; set; }
        }
        public class UserDeptAssignment()
        {
            public required int departmentId { get; set; }
        }
        class userLdapInfo()
        {
            public required bool Authenticated { get; set; }
            public userInfo? UserInfo { get; set; }
            public string? Response { get; set; }
        }
        class userInfo()
        {
            public required string FirstName { get; set; }
            public required string LastName { get; set; }
            public required string DisplayName { get; set; }
            public required string Description { get; set; }
            public required string Username { get; set; }
            public required string Office { get; set; }
            public required string Email { get; set; }
            public required string Street { get; set; }
            public required string City { get; set; }
            public required string State { get; set; }
            public required string ZipCode { get; set; }
            public required string Country { get; set; }
            public required string Home { get; set; }
            public required string Mobile { get; set; }

        }
    }
}
