context: using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Configuration;
  using System.Linq;
  using System.Text;
  using System.Web;
  using System.Web.Mvc;
  using System.Xml.Serialization;
  using CmsData;
  using CmsData.Classes;
  using CmsData.Finance;
  using CmsData.Registration;
  using CmsWeb.Areas.OnlineReg.Controllers;
  using CmsWeb.Code;
  using CmsWeb.Constants;
  using CmsWeb.Models;
  using Dapper;
  using Microsoft.Scripting.Utils;
  using Newtonsoft.Json;
  using UtilityExtensions;
  
  namespace CmsWeb.Areas.OnlineReg.Models
  {
      [Serializable]
      public class ManageGivingModel : IDbBinder
      {
          [NonSerialized]
          private CMSDataContext _currentDatabase;
          [JsonIgnore]
          public CMSDataContext CurrentDatabase
          {
              get => _currentDatabase ?? (CurrentDatabase = DbUtil.Db);
              set
              {
                  if (_currentDatabase == null)
                  {
                      _currentDatabase = value;
                  }
              }
          }
  
          public int pid { get; set; }
          public int orgid { get; set; }
  
          public void SetCurrentDatabase(CMSDataContext db)
          {
              CurrentDatabase = db;
          }
  
          public IList<string> DefaultFundIds = new List<string>();
  
          public IList<string> FallbackDefaultFundIds = new List<string>();
  
          public string FirstDefaultFundName { get; set; }
  
          public string RepeatPattern { get; set; }
  
          [DisplayName("Start On or After")]
          public DateTime? StartWhen { get; set; }
  
          public bool StartWhenIsNew { get; set; } = true;
  
          public DateTime? StopWhen { get; set; }
          public string SemiEvery { get; set; }
  
          [DisplayName("Day 1 of Month")]
          public int? Day1 { get; set; }
  
          [DisplayName("Day 2 of Month")]
          public int? Day2 { get; set; }
  
          [DisplayName("Repeat every")]
          public int? EveryN { get; set; }
  
          public string Period { get; set; }
          public string Type { get; set; }
          public string CreditCard { get; set; }
          public DateTime? NextDate { get; set; }
  
          [DisplayName("Expires (MMYY)")]
          public string Expires { get; set; }
  
          public string CVV { get; set; }
          public string Routing { get; set; }
          public string Account { get; set; }
          public bool testing { get; set; }
          public decimal total { get; set; }
          public string HeadingLabel
          {
              get => CurrentDatabase.Setting("ManageGivingHeaderLabel", "Giving Opportunities");
              set { }
          }
  
          public string FirstName { get; set; }
          public string Middle { get; set; }
          public string LastName { get; set; }
          public string Suffix { get; set; }
          public string Address { get; set; }
          public string Address2 { get; set; }
          public string City { get; set; }
          public string State { get; set; }
          public string Country { get; set; }
  
          public bool useNewManageGivingBuilder => CurrentDatabase.Setting("UseNewManageGivingBuilder", true);
          public string debitcredit => CurrentDatabase.GetDebitCreditLabel(PaymentProcessTypes.RecurringGiving);
          public string recaptchaSiteKey => CurrentDatabase.Setting("googleReCaptchaSiteKey", ConfigurationManager.AppSettings["googleReCaptchaSiteKey"]);
          public bool useRecaptcha => CurrentDatabase.Setting("UseRecaptchaForManageGiving") && recaptchaSiteKey.HasValue();
  
          [JsonIgnore]
          public IEnumerable<SelectListItem> Countries
          {
              get
              {
                  var list = CodeValueModel.ConvertToSelect(CodeValueModel.GetCountryList().Where(c => c.Code != "NA"), null);
                  list.Insert(0, new SelectListItem { Text = "(not specified)", Value = "" });
                  return list;
              }
          }
  
          public string Zip { get; set; }
          public string Phone { get; set; }
  
          private Dictionary<int, decimal?> _fundItem = new Dictionary<int, decimal?>();
          public Dictionary<int, decimal?> FundItem
          {
              get { return _fundItem; }
              set { _fundItem = value; }
          }
  
          public decimal? FundItemValue(int n)
          {
              if (FundItem.ContainsKey(n))
                  return FundItem[n];
              return null;
          }
  
          [NonSerialized]
          private Person _person;
          [JsonIgnore]
          public Person person => _person ?? (_person = CurrentDatabase.LoadPersonById(pid));
  
          [NonSerialized]
          private Organization _organization;
          [JsonIgnore]
          public Organization Organization => _organization ?? (_organization = CurrentDatabase.Organizations.Single(d => d.OrganizationId == orgid));
  
          [NonSerialized]
          private Settings _setting;
          [JsonIgnore]
          public Settings Setting => _setting ?? (_setting = CurrentDatabase.CreateRegistrationSettings(orgid));
  
          bool? _AcceptCredit;
          [JsonIgnore]
          public bool AcceptCredit => _AcceptCredit.HasValue ? _AcceptCredit.Value : (_AcceptCredit = CurrentDatabase.PaymentProcess
                  .Where(x => x.ProcessId == (int)PaymentProcessTypes.RecurringGiving)
                  .Select(x => x.AcceptCredit || x.AcceptDebit).Single()).Value;
  
          bool? _AcceptACH;
          [JsonIgnore]
          public bool AcceptACH
          {
              get => _AcceptACH.HasValue ? _AcceptACH.Value : (_AcceptACH = CurrentDatabase.PaymentProcess
                  .Where(x => x.ProcessId == (int)PaymentProcessTypes.RecurringGiving)
                  .Select(x => x.AcceptACH).Single()).Value;
          }
  
          [JsonIgnore]
          public string SpecialGivingFundsHeader => CurrentDatabase.Setting("SpecialGivingFundsHeader", "Special Giving Funds");
  
          [JsonIgnore]
          public bool HasManagedGiving => person?.ManagedGiving() != null;
  
          [Obsolete(Errors.ModelBindingConstructorError, true)]
          public ManageGivingModel() { }
          public ManageGivingModel(CMSDataContext db)
          {
              CurrentDatabase = db;
          }
  
          public ManageGivingModel(string host, int pid, int orgid = 0, string defaultFundIds = "")
              : this(CMSDataContext.Create(host))
          {
              this.pid = pid;
              this.orgid = orgid;
  
              if (person == null)
                  return;
  
              PopulateDefaultFundIds(host, defaultFundIds, person);
  
              var rg = person.ManagedGiving();
              if (rg != null)
                  PopulateSetup(rg);
              else if (Util.HasValue(Setting.ExtraValueFeeName))
                  PopulateExtraValueDefaults();
              else
                  PopulateReasonableDefaults();
  
              var pi = PopulatePaymentInfo();
              PopulateBillingName(pi);
              PopulateBillingAddress(pi);
  
          }
  
          private void PopulateDefaultFundIds(string host, string defaultFundIds, Person person)
          {
              var db = CMSDataContext.Create(host, asReadOnly: true);
              var setting = $"DefaultCampusFunds-{person.CampusId.GetValueOrDefault(0)}";
              var fundIdsForCampus = db.Setting(setting, string.Empty);
              if (string.IsNullOrWhiteSpace(defaultFundIds) && fundIdsForCampus.HasValue())
              {
                  // use campus default fund mapping if present.
                  defaultFundIds = fundIdsForCampus;
              }
  
              if (!string.IsNullOrWhiteSpace(defaultFundIds))
              {
                  var list = defaultFundIds.Split(',');
                  DefaultFundIds.AddRange(list);
              }
  
              if (DefaultFundIds.Any())
              {
                  FirstDefaultFundName = OnlineRegPersonModel.GetFundName(db, DefaultFundIds.First().ToInt());
              }
  
              var fundList = OnlineRegPersonModel.PrimaryFundList(db);
              FallbackDefaultFundIds.AddRange(fundList.Where(f => !DefaultFundIds.Contains(f.Value)).Select(f => f.Value));
          }
  
          private void PopulateBillingName(PaymentInfo pi)
          {
              FirstName = pi.FirstName ?? person.FirstName;
              Middle = (pi.MiddleInitial ?? person.MiddleName).Truncate(1);
              LastName = pi.LastName ?? person.LastName;
              Suffix = pi.Suffix ?? person.SuffixCode;
          }
  
          private void PopulateBillingAddress(PaymentInfo pi)
          {
              Address = pi.Address ?? person.PrimaryAddress;
              Address2 = pi.Address2 ?? person.PrimaryAddress2;
              City = pi.City ?? person.PrimaryCity;
              State = pi.State ?? person.PrimaryState;
              Country = pi.Country ?? person.PrimaryCountry;
              Zip = pi.Zip ?? person.PrimaryZip;
              Phone = pi.Phone ?? person.HomePhone ?? person.CellPhone;
          }
  
          private PaymentInfo PopulatePaymentInfo()
          {
              var accountId = MultipleGatewayUtils.GetAccount(CurrentDatabase, PaymentProcessTypes.RecurringGiving)?.GatewayAccountId;
              var pi = person.PaymentInfo(accountId ?? 0);
              if (pi == null)
              {
                  return new PaymentInfo() { GatewayAccountId = accountId ?? 0 };
              }
              CreditCard = pi.MaskedCard;
              Account = pi.MaskedAccount;
              Expires = pi.Expires;
              Routing = Util.Mask(new StringBuilder(pi.Routing), 2);
              Type = pi.PreferredGivingType;
              if (!AcceptCredit)
              {
                  Type = PaymentType.Ach; // bank account only
              }
              else if (!AcceptACH)
              {
                  Type = PaymentType.CreditCard; // credit card only
              }
              Type = AcceptACH ? Type : PaymentType.CreditCard;
              ClearMaskedNumbers(pi);
              total = FundItem.Sum(ff => ff.Value) ?? 0;
  
              return pi;
          }
  
          private void PopulateExtraValueDefaults()
          {
              var f = OnlineRegPersonModel.FullFundList(CurrentDatabase).SingleOrDefault(ff => ff.Text == Setting.ExtraValueFeeName);
              PopulateReasonableDefaults();
  
              var evamt = person.GetExtra(Setting.ExtraValueFeeName).ToDecimal();
              if (f != null && evamt > 0)
                  FundItem.Add(f.Value.ToInt(), evamt);
          }
  
          private void PopulateReasonableDefaults()
          {
              // reasonable defaults
              RepeatPattern = "M";
              Period = "M";
              SemiEvery = "E";
              EveryN = 1;
              StartWhen = DateTime.Today.AddDays(1);
          }
  
          private void PopulateSetup(ManagedGiving rg)
          {
              RepeatPattern = rg.SemiEvery != "S" ? rg.Period : rg.SemiEvery;
              SemiEvery = rg.SemiEvery;
              StartWhen = rg.StartWhen;
              StartWhenIsNew = false;
              StopWhen = null; //rg.StopWhen;
              Day1 = rg.Day1;
              Day2 = rg.Day2;
              EveryN = (rg.EveryN == 1 || rg.EveryN == 2) ? rg.EveryN : 1;
              Period = rg.Period;
              foreach (var ra in person.RecurringAmounts.AsEnumerable())
                  FundItem.Add(ra.FundId, ra.Amt);
  
              NextDate = rg.NextDate;
          }
  
          private void ClearMaskedNumbers(PaymentInfo pi)
          {
              var clearBankDetails = false;
              var clearCreditCardDetails = false;
  
              int? GatewayId = MultipleGatewayUtils.GatewayId(CurrentDatabase, PaymentProcessTypes.RecurringGiving);
              switch (GatewayId)
              {
                  case (int)GatewayTypes.Sage:
                      clearBankDetails = !pi.SageBankGuid.HasValue;
                      clearCreditCardDetails = !pi.SageCardGuid.HasValue;
                      break;
                  case (int)GatewayTypes.Transnational:
                      clearBankDetails = !pi.TbnBankVaultId.HasValue;
                      clearCreditCardDetails = !pi.TbnCardVaultId.HasValue;
                      break;
                  // case (int)GatewayTypes.Acceptiva:
                  // return new AcceptivaGateway(this, testing, ProcessType);
                  case (int)GatewayTypes.AuthorizeNet:
                      clearBankDetails = !pi.AuNetCustPayBankId.HasValue;
                      clearCreditCardDetails = !pi.AuNetCustPayId.HasValue;
                      break;
                  default:
                      break;
              }
  
              if (clearBankDetails)
              {
                  Account = string.Empty;
                  Routing = string.Empty;
              }
  
              if (clearCreditCardDetails)
              {
                  CreditCard = string.Empty;
                  CVV = string.Empty;
                  Expires = string.Empty;
              }
          }
  
          public void Confirm(Controller controller)
          {
              var db = CMSDataContext.Create(controller.HttpContext);
              var details = ViewExtensions2.RenderPartialViewToString(controller, "ManageGiving/EmailConfirmation", this);
  
              var staff = db.StaffPeopleForOrg(orgid);
              var from = staff[0];
  
              if (!string.IsNullOrEmpty(Setting.Body))
              {
                  var text = Setting.Body.Replace("{church}", db.Setting("NameOfChurch", "church"),
                      ignoreCase: true);
                  //            text = text.Replace("{name}", person.Name, ignoreCase: true);
                  text = text.Replace("{date}", DateTime.Now.ToString("d"), ignoreCase: true);
                  text = text.Replace("{email}", person.EmailAddress, ignoreCase: true);
                  text = text.Replace("{phone}", person.HomePhone.FmtFone(), ignoreCase: true);
  
gt:                 text = text.Replace("{contact}", from.Name, ignoreCase: true);