using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using CommonHelpers;

using MyFlat.DB;

namespace MyFlat {

//**************************************************************************************************
partial class HomeController {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <summary>
/// Login action processing
/// </summary>
/// <remarks>
/// Successful completion assumes, that new user-session object will be created for logged user.
/// </remarks>
async Task<ActionResult> OpenUserSession_A(HomePageInput input)
{
   HomePageModel PRESERVE_INPUT(HomePageModel pm)
   {
      pm.InputPreserve = new HomePageInputBase( input );
      return pm;
   }
   ViewResult EXPLICIT_ERROR(string message)
   {
      CON.PrintError( message );
      return View(PRESERVE_INPUT( new HomePageModel( this, message ) ));
      // Error message will be necessarily shown (in Debug, Release and “Expose”).
   }
/*
   ViewResult FROM_DB_ERROR(Exception e)
   {
      //CON.PrintError( HException.ComposeChainedMessage( e, true ) ); // with stack trace
      return View(PRESERVE_INPUT( HomePageModel.ServerError( this, e ) ));
      // Only “Expose”-version (Development) will show message(s) of unexpected DB-error.
   }
*/
   var app = new App( this );
   app.RemoveSessionData();
   app.RemoveOldSessions();

   var errorMessages = new List<string>();

// ANALYZING LOGIN INPUT:
   string accountCodeStr = (input.AccountCode ?? "").TrimEnd();
   if ( string.IsNullOrEmpty( accountCodeStr ) )
      errorMessages.Add(HLocal.TEXT(
         "Account code is empty.",
         "Пустой идентификатор Л/С." ));
   else
   if ( accountCodeStr.StartsWith(' ') )
      errorMessages.Add(HLocal.TEXT(
         "Account code must not start with space(s).",
         "Лицевой счёт не должен начинаться с пробела(ов)." ));
   else
   {
      bool isSuitableAccCode = accountCodeStr.Length == 10;
      foreach (char ch in accountCodeStr)
      {
         if ( !isSuitableAccCode ) break;
         if ( !char.IsDigit( ch ) )
            isSuitableAccCode = false;
      }
      if ( !isSuitableAccCode )
         errorMessages.Add(HLocal.TEXT(
            "Account code must contain exactly 10 digits.",
            "Лицевой счёт должен содержать ровно 10 цифр." ));
   }
   long accountCode = -1;
   if ( errorMessages.Count == 0 && !(
      accountCodeStr.Length == 10 &&
      long.TryParse( accountCodeStr, out accountCode ) &&
      accountCode >= 0
   ))
      errorMessages.Add(HLocal.TEXT(
         "Incorrect account code. (Exactly 10 digits were expected.)",
         "Некоректный идентификатор Л/С. (Ожидалось ровно 10 цифр.)" ));
   else
      if ( accountCode == 0 )
         errorMessages.Add(HLocal.TEXT(
            "Positive account code (>0) was expected.",
            "Ожидался положительный (>0) номер Л/С." ));
   string accessCode = input.AccessCode ?? "";
   if ( accessCode == "" )
      errorMessages.Add(HLocal.TEXT(
         "User access code was not entered.",
         "Код доступа не введён." ));

   if ( errorMessages.Count > 0 )
   {
      CON.PrintWarning(HLocal.TEXT(
         "Login input is incorrect.",
         "Некорректный ввод для процедуры логин-а." ));
      return View(PRESERVE_INPUT(
         new HomePageModel( this, errorMessages ){
            AreLoginInputErrors = true }));
   }

// Performing authorization query:
   var spr = Authorization_SP.InvalidResult; // instead of explicit bulky result-type indication
   var err = await Attempt_A(async()=>{
      spr = await Authorization_SP.EXEC_A( accountCode, accessCode ); });
   if ( err != null )
      goto FROM_DB_ERROR;
   // In case of expected DB-error — explicit messages from SP:
   if ( !spr.Success )
   {
      // SP-status checking:
      if ( spr.Status == Authorization_SP.Status_ServiceNotAvailable )
         CON.PrintExpressive(HLocal.TEXT(
            "The service is unavailable (test error):",
            "Сервис недоступен (тестовая ошибка):" ));
      return EXPLICIT_ERROR( spr.ErrorMessage ); // proper error message is allways available
      // Error message will be necessarily shown (in Debug, Release and “Expose”).
   }
   // Taking the only record from first and second result-sets:
   var auth = spr.List_1[0]; // no exception is expected here
   var acc = spr.List_2[0]; // no exception is expected in this place
   // Creating authorization main-info object, using both (#1+#2) result-sets:
   var ai = new AuthorizationInfo( accountCodeStr, acc.Initials, false, auth.RegistrationDate );

// Obtain flat service list (additional queries):
   string? message = null;
   var r2 = GetServiceList_SP.InvalidResult; // instead of explicit result-type indication
   err = await Attempt_A(async()=>{
      (r2,message) = await GetServiceList_SP.EXEC_A(); });
   if ( err != null )
      goto FROM_DB_ERROR;
   if ( !r2.Success )
      return EXPLICIT_ERROR( r2.ErrorMessage ); // proper error message is allways available
// Obtain financial month info (current fin.term):
   var r3 = GetFinTermInfo_SP.InvalidResult; // instead of explicit result-type indication
   err = await Attempt_A(async()=>{
      r3 = await GetFinTermInfo_SP.EXEC_A(); });
   if ( err != null )
      goto FROM_DB_ERROR;
   // Openning user session for our “state-full” service:
   app.CreateUserSession( auth.AccountId, auth.AccountCode, acc, ai );

// Print information to service console:
   var fintermInfo = r3.Output; // no exception is expected here
   CON.PrintExclamation(HLocal.TEXT(
      "Acc.code: {0}",
      "Код Л/С: {0}" ),
      input.AccountCode );
   CON.PrintIntensive( acc.Initials );
   CON.PrintIntensive( acc.Address );
   CON.PrintExpressive( fintermInfo.Name );
   if ( message != null )
      CON.PrintWarning( message );
   else
      CON.Print(HLocal.TEXT(
         "Service count: {0} (all possible)",
         "Число услуг: {0} (все возможные)" ),
         r2.List.Count ); // no exception is expected here

   return View( new HomePageModel( this ) ); // show homepage for logged user

FROM_DB_ERROR:
   return View(PRESERVE_INPUT( HomePageModel.ServerError( this, err ) ));
   // Database error details will only be shown in “Expose” (development environment).
}

/// <summary>
/// Log-off action processing
/// </summary>
/// <remarks>
/// User-session object (if exists) will be removed.
/// </remarks>
ActionResult CloseUserSession()
{
   var app = new App( this );
   string accountCodeStr = app.LoginName;
   app.RemoveSessionData();
   app.RemoveOldSessions();
   CON.PrintExclamation(HLocal.TEXT(
      "User session has been closed (#{0}).",
      "Пользовательская сессия закрыта (#{0})." ),
      accountCodeStr );
   return View();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HomeController
//**************************************************************************************************

} // MyFlat
