unit Utils;

interface

uses
  System.Classes, SysUtils, JS, Web, WEBLib.Forms, WEBLib.Toast, DateUtils, WebLib.Dialogs;

procedure ShowStatusMessage(const AMessage, AClass: string; const AElementId: string);
procedure HideStatusMessage(const AElementId: string);
procedure ShowSpinner(SpinnerID: string);
procedure HideSpinner(SpinnerID: string);
procedure ShowErrorModal(msg: string);
procedure ShowToast(const MessageText: string; const ToastType: string = 'success');
procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
procedure ShowNotificationModal(msg: string);
procedure ShowAppDialog(const Msg: string; const Title: string = 'emT3 web app'; ReloadOnClose: Boolean = False; ButtonText: string = 'Close');
function NormalizeDateValue(const Value: string): string;


implementation

procedure ShowStatusMessage(const AMessage, AClass: string; const AElementId: string);
var
  StatusMessage: TJSHTMLElement;
begin
  StatusMessage := TJSHTMLElement(document.getElementById(AElementId));
  if Assigned(StatusMessage) then
  begin
    if AMessage = '' then
    begin
      StatusMessage.style.setProperty('display', 'none');
      StatusMessage.className := '';
      StatusMessage.innerHTML := '';
    end
    else
    begin
      StatusMessage.innerHTML := AMessage;
      StatusMessage.className := 'alert ' + AClass;
      StatusMessage.style.setProperty('display', 'block');
    end
  end
  else
    console.log('Error: Status message element not found');
end;


procedure HideStatusMessage(const AElementId: string);
var
  StatusMessage: TJSHTMLElement;
begin
  StatusMessage := TJSHTMLElement(document.getElementById(AElementId));
  if Assigned(StatusMessage) then
  begin
    StatusMessage.style.setProperty('display', 'none');
    StatusMessage.className := '';
    StatusMessage.innerHTML := '';
  end
  else
    console.log('Error: Status message element not found');
end;


procedure ShowSpinner(SpinnerID: string);
var
  SpinnerElement: TJSHTMLElement;
begin
  SpinnerElement := TJSHTMLElement(document.getElementById(SpinnerID));
  if Assigned(SpinnerElement) then
  begin
    // Move spinner to the <body> if it's not already there
    asm
      if (SpinnerElement.parentNode !== document.body) {
        document.body.appendChild(SpinnerElement);
      }
    end;

    SpinnerElement.classList.remove('d-none');
    SpinnerElement.classList.add('d-block');
  end;
end;

procedure HideSpinner(SpinnerID: string);
var
  SpinnerElement: TJSHTMLElement;
begin
  SpinnerElement := TJSHTMLElement(document.getElementById(SpinnerID));
  if Assigned(SpinnerElement) then
  begin
    SpinnerElement.classList.remove('d-block');
    SpinnerElement.classList.add('d-none');
  end;
end;


procedure ShowErrorModal(msg: string);
begin
  asm
    var modal = document.getElementById('main_errormodal');
    var label = document.getElementById('main_lblmodal_body');
    var reloadBtn = document.getElementById('btn_modal_restart');

    if (label) label.innerText = msg;

    // Ensure modal is a direct child of <body>
    if (modal && modal.parentNode !== document.body) {
      document.body.appendChild(modal);
    }

    // Bind hard reload to button
    if (reloadBtn) {
      reloadBtn.onclick = function () {
        window.location.reload(true); // hard reload, bypass cache
      };
    }

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


procedure ShowNotificationModal(msg: string);
begin
  asm
    var modal = document.getElementById('main_notification_modal');
    var label = document.getElementById('main_notification_modal_body');
    var closeBtn = document.getElementById('btn_modal_close');

    if (label) label.innerText = msg;

    // Ensure modal is a direct child of <body>
    if (modal && modal.parentNode !== document.body) {
      document.body.appendChild(modal);
    }

    // Button simply closes the modal
    if (closeBtn) {
      closeBtn.onclick = function () {
        var existing = bootstrap.Modal.getInstance(modal);
        if (existing) {
          existing.hide();
        }
      };
    }

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


procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
begin
  asm
    var modal = document.getElementById('main_confirmation_modal');
    var body = document.getElementById('main_modal_body');
    var btnLeft = document.getElementById('btn_confirm_left');
    var btnRight = document.getElementById('btn_confirm_right');
    var bsModal;

    if (body) body.innerText = msg;
    if (btnLeft) btnLeft.innerText = leftLabel;
    if (btnRight) btnRight.innerText = rightLabel;

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

    btnLeft.onclick = null;
    btnRight.onclick = null;

    btnLeft.onclick = function () {
      bsModal.hide();
      ConfirmProc(true); // user confirmed
    };

    btnRight.onclick = function () {
      bsModal.hide();
      ConfirmProc(false); // user canceled
    };

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


procedure ShowAppDialog(const Msg: string; const Title: string = 'emT3 web app'; ReloadOnClose: Boolean = False; ButtonText: string = 'Close');
begin
  asm
    var dlg = document.createElement("dialog");
    dlg.classList.add("shadow", "rounded", "border", "p-4");
    dlg.style.maxWidth = "500px";
    dlg.style.width = "90%";
    dlg.style.fontFamily = "system-ui, sans-serif";

    dlg.innerHTML =
      "<h5 class='fw-bold mb-3 text-danger'>" + Title + "</h5>" +
      "<p class='mb-3' style='white-space: pre-wrap;'>" + Msg + "</p>" +
      "<div class='text-end'>" +
      "<button id='appDialogBtn' class='btn btn-primary'>" + ButtonText + "</button>" +
      "</div>";

    document.body.appendChild(dlg);
    dlg.showModal();

    document.getElementById("appDialogBtn").addEventListener("click", function () {
      if (ReloadOnClose) {
        location.reload();
      } else {
        dlg.close();
        dlg.remove();
      }
    });
  end;
end;


procedure ShowToast(const MessageText: string; const ToastType: string = 'success');
var
  ParsedText, ToastKind, MsgPrefix: string;
  Parts: TArray<string>;
begin
  ParsedText := MessageText.Trim;
  ToastKind := ToastType.ToLower;

  // Check for "Success:" or "Failure:" at the start of message
  if ParsedText.Contains(':') then
  begin
    Parts := ParsedText.Split([':'], 2);
    MsgPrefix := Parts[0].Trim.ToLower;

    if (MsgPrefix = 'success') or (MsgPrefix = 'failure') then
    begin
      ParsedText := Parts[1].Trim;

      if MsgPrefix = 'success' then
        ToastKind := 'success'
      else
        ToastKind := 'danger';
    end;
  end;

  asm
    var toastEl = document.getElementById('bootstrapToast');
    var toastBody = document.getElementById('bootstrapToastBody');

    if (!toastEl || !toastBody) return;

    toastBody.innerText = ParsedText;

    toastEl.classList.remove('bg-success', 'bg-danger', 'bg-warning', 'bg-primary');
    toastEl.classList.remove('slide-in');

    switch (ToastKind) {
      case 'danger':
        toastEl.classList.add('bg-danger');
        break;
      case 'warning':
        toastEl.classList.add('bg-warning');
        break;
      case 'info':
        toastEl.classList.add('bg-primary');
        break;
      default:
        toastEl.classList.add('bg-success');
    }

    // Add slide-in animation
    toastEl.classList.add('slide-in');

    var toast = new bootstrap.Toast(toastEl, { delay: 2500 });
    toast.show();

    // Remove animation class after it's done (so it can be reapplied)
    setTimeout(function() {
      toastEl.classList.remove('slide-in');
    }, 500);
  end;
end;


function NormalizeDateValue(const Value: string): string;
var
  normalizedValue: string;
begin
  normalizedValue := Trim(Value);

  if (normalizedValue = '') or
     SameText(normalizedValue, '1899-12-30') or
     SameText(normalizedValue, '12/30/1899') or
     SameText(normalizedValue, '1899-12-30T00:00:00') or
     SameText(normalizedValue, '1899-12-30 00:00:00') then
    Result := ''
  else
    Result := normalizedValue;
end;


end.

