using System.Net;

namespace SummerBestWebForm2.SessionState;

// INTERFACE

public interface ISessionManager
{
  Task<MdlSession> GetSession();
  Task UpdateSession(MdlSession session);
}

// CODE

/// <summary>
/// Dictionary containing per-user session objects, keyed
/// by sessionId.
/// </summary>
public class SessionManager : ISessionManager
{
  private Dictionary<Guid, MdlSession> _sessions = new Dictionary<Guid, MdlSession>();
  private readonly ISessionIdManager _sessionIdManager;
  private object syncLock1 = new();

  public SessionManager(ISessionIdManager sessionIdManager)
  {
    _sessionIdManager = sessionIdManager;
  }

  public async Task<MdlSession> GetSession()
  {
    string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
    string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
    Guid theSessionId;

    if (!Guid.TryParse(key, out theSessionId))
      theSessionId = Guid.Empty;//Guid.Parse(key);

    MdlSession session;
    bool needToCreateNew = false;

    if (!_sessions.ContainsKey(theSessionId))
    {
      needToCreateNew = true;
    }
    else if (_sessions[theSessionId].dtExpires < DateTimeOffset.Now)
      needToCreateNew = true;

    if (needToCreateNew)
    {
      session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
      _sessions.Add(theSessionId, new MdlSession());
    }
    else
      session = _sessions[theSessionId];

    // ensure session isn't checked out by wasm
    //while (session.IsCheckedOut)
    //    await Task.Delay(5);
    var endTime = DateTime.UtcNow.AddSeconds(10);
    while (session.IsCheckedOut)
    {
      if (DateTime.UtcNow > endTime)
        throw new TimeoutException();
      await Task.Delay(5);
    }

    return session;
  }

  public async Task UpdateSession(MdlSession session)
  {
    if (session != null)
    {
      string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
      string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
      Guid theSessionId;

      if (!Guid.TryParse(key, out theSessionId))
        theSessionId = Guid.Empty;//Guid.Parse(key);

      if (_sessions.ContainsKey(theSessionId))
      {
        //session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
        session.SessionId = key;
        session.dtExpires = DateTimeOffset.Now.AddHours(36);  // Rolling expiration date
        _sessions[theSessionId] = session;
        _sessions[theSessionId].IsCheckedOut = false;
      }
      else
      {
        var sess = new MdlSession() { SessionId = key, IPAddress = ipAddress };
        _sessions[theSessionId] = session;
      }

      // Remove stale sessions
      lock (syncLock1)
      {
        try
        {
          var lstRemove = new HashSet<Guid>();
          foreach (var kvp in _sessions)
          {
            if (kvp.Value.dtExpires < DateTimeOffset.Now && !lstRemove.Contains(kvp.Key))
              lstRemove.Add(kvp.Key);
          }
          foreach (var rmv in lstRemove)
            _sessions.Remove(rmv);
          lstRemove.Clear();
        }
        catch (Exception ex)
        {
          // do something
        }
      }
    }
  } //UpdateSession


  /// <summary>
  /// Replace the contents of oldSession with the items
  /// in newSession.
  /// </summary>
  /// <param name="newSession"></param>
  /// <param name="oldSession"></param>
  private void Replace(MdlSession newSession, MdlSession oldSession)
  {
    oldSession.Clear();
    foreach (var key in newSession.Keys)
      oldSession.Add(key, newSession[key]);
  }
}
