// Form that functions as both a way to edit or add users to the database
// Author: Cameron Hayes

unit View.EditUser;

interface

uses
  System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
  WEBLib.Forms, WEBLib.Dialogs, Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls,
  WEBLib.DBCtrls, XData.Web.Client, WEBLib.ExtCtrls, Vcl.Menus, WEBLib.Menus;

type
  TFViewEditUser = class(TWebForm)
    WebLabel1: TWebLabel;
    WebLabel2: TWebLabel;
    WebLabel3: TWebLabel;
    WebLabel4: TWebLabel;
    WebLabel5: TWebLabel;
    WebLabel6: TWebLabel;
    WebLabel7: TWebLabel;
    edtPhoneNumber: TWebEdit;
    edtConfirmPassword: TWebEdit;
    edtEmail: TWebEdit;
    edtPassword: TWebEdit;
    cbAdmin: TWebCheckBox;
    btnConfirm: TWebButton;
    edtFullname: TWebEdit;
    edtUsername: TWebEdit;
    XDataWebClient1: TXDataWebClient;
    btnCancel: TWebButton;
    WebTimer1: TWebTimer;
    btnConfirmChanges: TWebButton;
    pnlMessage: TWebPanel;
    lblMessage: TWebLabel;
    btnCloseNotification: TWebButton;
    lblactive: TWebLabel;
    cbActive: TWebCheckBox;
    procedure WebFormCreate(Sender: TObject);
    procedure btnConfirmClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure WebTimer1Timer(Sender: TObject);
    [async] procedure btnConfirmChangesClick(Sender: TObject);
    procedure btnCloseNotificationClick(Sender: TObject);
  private
    { Private declarations }
    FMessage: string;
    Mode: string;
    Username: string;
    FullName: string;
    Phone: string;
    Email: string;
    Admin: boolean;
    Active: boolean;
    [async] procedure EditUser();
    [async] function AddUser(): string;
    procedure HideNotification();
    procedure ShowNotification(notification: string);
  public
    { Public declarations }
    Info: string;
    class function CreateForm(AElementID, Mode, Username, FullName, Phone, Email: string; Admin, Active: boolean): TWebForm;
  end;

var
  FViewEditUser: TFViewEditUser;

implementation

uses
Windows,
View.Main,
View.Users,
VCL.Dialogs,
ConnectionModule,
Utils;

procedure TFViewEditUser.btnCancelClick(Sender: TObject);
// Cancels the edit or addition
begin
  Info := 'Failure:Changes discarded!';
  FViewMain.ShowUserForm(Info);
end;

procedure TFViewEditUser.btnCloseNotificationClick(Sender: TObject);
begin
  HideNotification;
end;

[async]
procedure TFViewEditUser.btnConfirmChangesClick(Sender: TObject);
begin
  console.log('btnConfirmChangesClick triggered');
  Utils.ShowSpinner('spinner');

  if Mode = 'Edit' then
  begin
    console.log('Calling EditUser()');
    await(EditUser());
  end
  else
  begin
    console.log('Calling AddUser()');
    await(AddUser());
  end;

  console.log('Info after operation: ', Info);
  WebTimer1.Enabled := true;
end;



[async] function TFViewEditUser.AddUser(): string;
var
  userInfo: string;
  xdcResponse: TXDataClientResponse;
  responseString: TJSObject;
begin
  userInfo := '&username=' + string(edtUsername.Text).ToLower +
              '&password=' + edtPassword.Text +
              '&fullname=' + edtFullName.Text +
              '&phonenumber=' +  edtPhoneNumber.Text +
              '&email=' + edtEmail.Text +
              '&admin=' + BoolToStr(cbAdmin.Checked);

  console.log('AddUser -> userInfo: ', userInfo);

  xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.AddUser', [userInfo]));
  responseString :=  TJSObject(xdcResponse.Result);
  Info := string(responseString['value']);

  console.log('AddUser -> response Info: ', Info);
end;


procedure TFViewEditUser.HideNotification;
begin
  pnlMessage.ElementHandle.hidden := True;
end;

procedure TFViewEditUser.ShowNotification(Notification: string);
begin
  if Notification <> '' then
  begin
    lblMessage.Caption := Notification;
    pnlMessage.ElementHandle.hidden := False;
  end;
end;

[async] procedure TFViewEditUser.EditUser();
var
  editOptions: string;
  xdcResponse: TXDataClientResponse;
  responseString: TJSObject;
begin
  editOptions := 'username=' + Username +
                 '&fullname=' + edtFullName.Text +
                 '&phonenumber=' + edtPhoneNumber.Text +
                 '&email=' + edtEmail.Text +
                 '&admin=' + BoolToStr(cbAdmin.Checked) +
                 '&newuser=' + edtUsername.Text +
                 '&password=' + edtPassword.Text +
                 '&active=' + BoolToStr(cbActive.Checked);

  console.log('EditUser -> editOptions: ', editOptions);

  xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.EditUser', [editOptions]));
  responseString :=  TJSObject(xdcResponse.Result);
  Info := string(responseString['value']);

  console.log('EditUser -> response Info: ', Info);
end;


class function TFViewEditUser.CreateForm(AElementID, Mode, Username, FullName, Phone, Email: string; Admin, Active: boolean): TWebForm;
// Autofills known information about a user on create
  procedure AfterCreate(AForm: TObject);
  begin
    TFViewEditUser(AForm).Mode := Mode;
    TFViewEditUser(AForm).Username := Username;
    TFViewEditUser(AForm).FullName := FullName;
    TFViewEditUser(AForm).Phone := Phone;
    TFViewEditUser(AForm).Email:= Email;
    TFViewEditUser(AForm).Admin := Admin;
    TFViewEditUser(AForm).Active := Active;
  end;

{$R *.dfm}
begin
  Application.CreateForm(TFViewEditUser, AElementID, Result, @AfterCreate);
end;



procedure TFViewEditUser.WebFormCreate(Sender: TObject);
// Autofills known information about a user on create
begin
  if not Assigned(DMConnection) then
    Application.CreateForm(TDMConnection, DMConnection);
  if FMessage <> '' then
    ShowNotification(FMessage)
  else
    HideNotification;
  edtUsername.Text := Username;
  edtFullName.Text := FullName;
  if Mode = 'Edit' then
  begin
    edtPassword.Text := 'Hidden';
    edtConfirmPassword.Text := 'Hidden';
  end
  else
  edtPhoneNumber.Text := Phone;
  edtEmail.Text :=  Email;
  cbAdmin.checked := Admin;
  cbActive.Checked := Active;
end;

procedure TFViewEditUser.WebTimer1Timer(Sender: TObject);
begin
  WebTimer1.Enabled := False;
  Utils.HideSpinner('spinner');

  console.log('WebTimer1Timer triggered. Info = ', Info);

  if (not Info.Contains('Failure')) then
  begin
    console.log('Navigating back to user list...');
    FViewMain.ShowUserForm(Info);
  end
  else
  begin
    console.log('Showing notification instead of redirect');
    showNotification(Info);
  end;
end;


procedure TFViewEditUser.btnConfirmClick(Sender: TObject);
var
  checkString: string;
  charIndex: Integer;
  phoneNum: string;
begin
  // Combine all inputs to quickly check for illegal characters like &
  checkString := edtFullName.Text + edtUsername.Text + edtPassword.Text +
                 edtConfirmPassword.Text + edtPhoneNumber.Text + edtEmail.Text;

  // Basic field presence checks
  if edtFullName.Text.Trim = '' then
  begin
    ShowNotification('Full Name field is blank!');
    Exit;
  end;

  if edtUsername.Text.Trim = '' then
  begin
    ShowNotification('Username field is blank!');
    Exit;
  end;

  if edtPassword.Text.Trim = '' then
  begin
    ShowNotification('Password field is blank!');
    Exit;
  end;

  if edtConfirmPassword.Text.Trim = '' then
  begin
    ShowNotification('Please confirm your password!');
    Exit;
  end;

  if edtPhoneNumber.Text.Trim = '' then
  begin
    ShowNotification('Phone Number field is blank!');
    Exit;
  end;

  if edtEmail.Text.Trim = '' then
  begin
    ShowNotification('Email field is blank!');
    Exit;
  end;

  // Special character check
  if checkString.Contains('&') then
  begin
    ShowNotification('No fields may contain "&"!');
    Exit;
  end;

  // Email format validation
  if not edtEmail.Text.Contains('@') or
     (TJSArray(edtEmail.Text.Split(['@'])).length <> 2) or
     (edtEmail.Text.CountChar('@') > 1) then
  begin
    ShowNotification('Please enter a valid email address');
    Exit;
  end;

  // Phone number validation (allow spaces, dashes, parentheses, etc.)
  phoneNum := edtPhoneNumber.Text;
  phoneNum := phoneNum.Replace('(', '')
                      .Replace(')', '')
                      .Replace('-', '')
                      .Replace(' ', '')
                      .Trim;

  if Length(phoneNum) <> 10 then
  begin
    ShowNotification('Please enter a valid phone number');
    Exit;
  end;

  for charIndex := 1 to Length(phoneNum) do
  begin
    if not (phoneNum[charIndex] in ['0' .. '9']) then
    begin
      ShowNotification('Please enter a valid phone number');
      Exit;
    end;
  end;

  // Password match and length validation
  if edtPassword.Text <> edtConfirmPassword.Text then
  begin
    ShowNotification('Passwords must match!');
    Exit;
  end;

  if (Length(edtPassword.Text) > 20) or (Length(edtPassword.Text) < 6) then
  begin
    ShowNotification('Passwords must be between 6–20 characters!');
    Exit;
  end;

  // All validations passed, show modal
  asm
    var modal = document.getElementById('confirmation_modal');

    if (modal && modal.parentNode !== document.body) {
      document.body.appendChild(modal);
    }

    var bsModal = new bootstrap.Modal(modal, { keyboard: false });
    bsModal.show();
  end;
end;


end.
