using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
using e_suite.Database.Audit;
using eSuite.API.Extensions;
using eSuite.API.Middleware;
using eSuite.Core.Miscellaneous;
using IUserManager = e_suite.API.Common.IUserManager;
// ReSharper disable All

namespace eSuite.API.SingleSignOn;

/// <summary>
/// Internal class used to manage cookies
/// </summary>
public class CookieManager : ICookieManager
{
    private readonly IUserManager _userManager;

    /// <summary>
    /// Default constructor
    /// </summary>
    /// <param name="userManager"></param>
    public CookieManager(IUserManager userManager)
    {
        _userManager = userManager;
    }

    private const string ssoNewUserLinkCookieName = "eSuiteNewUserLinkCookie";
    private const string ssoProfileLinkCookieName = "eSuiteProfileLinkCookie";
    private const string ssoIdCookieName = "eSuiteSsoProvider";

    /// <summary>
    /// Creates the session cookie with appropriate settings
    /// </summary>
    /// <param name="response"></param>
    /// <param name="loginResponse"></param>
    /// <returns></returns>
    public Task CreateSessionCookie(HttpResponse response, LoginResponse loginResponse)
    {
        response.Cookies.Append(AuthenticationExtension.SessionCookieName, loginResponse.Token, new CookieOptions
        {
            HttpOnly = false, //Set False as Javascript (React) needs to read the cookie.
            SameSite = SameSiteMode.Strict,
            Secure = true,
            IsEssential = true,
            Expires = null, //Session Cookie
            Path = "/"
        });
        return Task.CompletedTask;
    }

    /// <summary>
    /// Deletes any existing session cookie
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    public Task DeleteSessionCookie(HttpResponse response)
    {
        response.Cookies.Delete(AuthenticationExtension.SessionCookieName);
        return Task.CompletedTask;
    }

    /// <summary>
    /// Creates cookie used with an Sso Identity to a user account
    /// </summary>
    /// <param name="response"></param>
    /// <param name="auditUserDetails"></param>
    /// <param name="generalIdRef"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task CreateProfileLinkCookie(HttpResponse response, AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
    {
        var guid = await _userManager.CreateSingleUseGuid(auditUserDetails, generalIdRef, cancellationToken);

        response.Cookies.Append(ssoProfileLinkCookieName, guid.ToString(), new CookieOptions
        {
            HttpOnly = true, //Set True only the server side app needs to know about this
            SameSite = SameSiteMode.Lax, //Used when returning from an sso authorisation
            Secure = true,
            IsEssential = true,
            Expires = null, //Session Cookie
            Path = "/"
        });
    }

    /// <summary>
    /// Creates cookie used with an Sso Identity to a user account
    /// </summary>
    /// <param name="response"></param>
    /// <param name="auditUserDetails"></param>
    /// <param name="generalIdRef"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task CreateNewUserLinkCookie(HttpResponse response, AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
    {
        var guid = await _userManager.CreateSingleUseGuid(auditUserDetails, generalIdRef, cancellationToken);

        response.Cookies.Append(ssoNewUserLinkCookieName, guid.ToString(), new CookieOptions
        {
            HttpOnly = true, //Set True only the server side app needs to know about this
            SameSite = SameSiteMode.Lax, //Used when returning from an sso authorisation
            Secure = true,
            IsEssential = true,
            Expires = null, //Session Cookie
            Path = "/"
        });
    }

    /// <summary>
    /// Used to find a profile link cookie and find the appropriate user account
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="NotFoundException"></exception>
    public async Task<CookieLink?> GetUserIdFromLinkCookie(HttpRequest request, CancellationToken cancellationToken)
    {
        var cookieContent = string.Empty;
        var cookieLink = new CookieLink()
        {
            LinkType = LinkType.None
        };
        
        if (request.Cookies.ContainsKey(ssoProfileLinkCookieName))
        {
            cookieContent = request.Cookies[ssoProfileLinkCookieName] ?? throw new NotFoundException();
            cookieLink.LinkType = LinkType.Profile;
        }

        if (request.Cookies.ContainsKey(ssoNewUserLinkCookieName))
        {
            cookieContent = request.Cookies[ssoNewUserLinkCookieName] ?? throw new NotFoundException();
            cookieLink.LinkType = LinkType.NewUser;
        }

        if (cookieLink.LinkType == LinkType.None)
        {
            return null;
        }

        var guid = new Guid(cookieContent);

        cookieLink.User = await _userManager.GetUserWithSingleUseGuid(guid, cancellationToken);
        return cookieLink;
    }

    /// <summary>
    /// Delete any existing Profile link cookie.
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    public Task DeleteLinkCookie(HttpResponse response)
    {
        response.Cookies.Delete(ssoProfileLinkCookieName);
        response.Cookies.Delete(ssoNewUserLinkCookieName);
        return Task.CompletedTask;
    }

    /// <summary>
    /// Find the SsoProvider Id using a cookie to save user input
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public Task<long?> GetSsoIdFromSsoIdCookie(HttpRequest request)
    {
        return Task.FromResult(request.Cookies.ContainsKey(ssoIdCookieName) ? request.Cookies[ssoIdCookieName].ToLong() : null);
    }

    /// <summary>
    /// Delete the SsoProvider cookie as the user has manually logged out
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    public Task DeleteSsoIdCookie(HttpResponse response)
    {
        response.Cookies.Delete(ssoIdCookieName);
        return Task.CompletedTask;
    }

    /// <summary>
    /// Create the SsoProvider cookie, so that the login provess can be sped up for user convenience
    /// </summary>
    /// <param name="response"></param>
    /// <param name="ssoId"></param>
    /// <returns></returns>
    public Task CreateSsoIdCookie(HttpResponse response, long ssoId)
    {
        response.Cookies.Append(ssoIdCookieName, ssoId.ToString(), new CookieOptions()
        {
            HttpOnly = true,
            SameSite = SameSiteMode.Strict,
            Secure = true,
            IsEssential = true,
            Expires = DateTime.UtcNow.AddMonths(1), //cookie will expire after a month.
            Path = "/"
        });
        return Task.CompletedTask;
    }
}