using BL;
using ITVisions.Blazor;
using Microsoft.AspNetCore.Components.Authorization;
using System;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Web
{
 /// <summary>
 /// AuthenticationStateProvider for MiracleList Blazor Server
 /// uses the MiracleList Business Logic (UserManager) for Authentication
 /// </summary>
 public class MLAuthenticationStateProvider : AuthenticationStateProvider
 {
  // DI
  private BlazorUtil blazorUtil { get; set; }
  private Blazored.LocalStorage.ILocalStorageService localStorage { get; set; }

  private BO.User currentUser { get; set; }

  // Name of local Storage Key
  const string LocalStorageKey = "MLToken";

  public MLAuthenticationStateProvider(BlazorUtil blazorUtil, Blazored.LocalStorage.ILocalStorageService localStorage)
  {
   // DI
   this.blazorUtil = blazorUtil;
   this.localStorage = localStorage;
  }

  /// <summary>
  /// Login to be called by Razor Component Login.razor in case of login!
  /// </summary> 
  public virtual async Task<BO.User> LogIn(string username, string password, string backend)
  {
   UserManager um = new UserManager();
   DAL.Context.ConnectionString = backend;
   um = new BL.UserManager(username, password);
   this.currentUser = um.CurrentUser;
   Notify();
   if (this.currentUser != null)
   {
    um.InitDefaultTasks();
    // Store user token in local Storage
    await localStorage.SetItemAsync(LocalStorageKey, currentUser.Token);
   }
   return this.currentUser;
  }

  /// <summary>
  /// Logout to be called by Razor Component Login.razor in case of logout!
  /// </summary>
  public virtual void Logout()
  {
   if (currentUser == null) return;
   blazorUtil.Log("Logout");
   UserManager.Logout(currentUser);
   this.currentUser = null;
   // Remove user token from local Storage
   localStorage.RemoveItemAsync(LocalStorageKey);
   Notify();
  }

  /// <summary>
  /// Notify Blazor infrastructure about new Authentication State
  /// </summary>
  private void Notify()
  {
   var aus = CreateAuthenticationState(currentUser);
   var e = Task.FromResult(aus);
   this.NotifyAuthenticationStateChanged(e);
  }

  /// <summary>
  /// Convert LoginInfo to AuthenticationState
  /// </summary>
  private AuthenticationState CreateAuthenticationState(BO.User u)
  {
   // If you create your identity including authenticationType parameter the user is authenticated. If you create your identity without authenticationType parameter, the user is not authenticated.
   if (u == null)
   { // not authenticated :-(
    return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
   }

   // authenticated :-)
   var identity = new ClaimsIdentity(new[]
  {
    new Claim("Backend",  DAL.Context.ConnectionString),
    new Claim("Server", new Microsoft.Data.SqlClient.SqlConnectionStringBuilder(DAL.Context.ConnectionString).DataSource),
    new Claim("LogInDateTime", DateTime.Now.ToString()),
    new Claim(ClaimTypes.Sid, u.UserID.ToString()),
    new Claim(ClaimTypes.Name, u.UserName),
  }, "MiracleList Authentication Type");

   var user = new ClaimsPrincipal(identity);
   return new AuthenticationState(user);
  }

  public override async Task<AuthenticationState> GetAuthenticationStateAsync()
  {
   if (currentUser == null)
   {
    blazorUtil.Log(nameof(GetAuthenticationStateAsync) + ": User not logged in!");

    try
    {
     string token = await localStorage.GetItemAsync<string>(LocalStorageKey);
     if (token != null) currentUser = new UserManager(token).CurrentUser;
    }
    catch (Exception)
    {

     blazorUtil.Log(nameof(GetAuthenticationStateAsync) + ": cannot access local storage!");
     currentUser = null;
    }

    if (currentUser == null)
    {
     blazorUtil.Log(nameof(GetAuthenticationStateAsync) + ": User not found in local Storage!");
     return CreateAuthenticationState(null); // null = kein User!
    }
   }

   blazorUtil.Log(nameof(GetAuthenticationStateAsync) + ": User=" + currentUser?.UserName);

   return CreateAuthenticationState(currentUser);
  }
 }
}