import { ReactiveCache } from '/imports/reactiveCache';

Utils = {
  setBackgroundImage(url) {
    const currentBoard = Utils.getCurrentBoard();
    if (currentBoard.backgroundImageURL !== undefined) {
      $(".board-wrapper").css({"background":"url(" + currentBoard.backgroundImageURL + ")","background-size":"cover"});
      $(".swimlane,.swimlane .list,.swimlane .list .list-body,.swimlane .list:first-child .list-body").css({"background-color":"transparent"});
      $(".minicard").css({"opacity": "0.9"});
    } else if (currentBoard["background-color"]) {
      currentBoard.setColor(currentBoard["background-color"]);
    }
  },
  /** returns the current board id
   * <li> returns the current board id or the board id of the popup card if set
   */
  getCurrentBoardId() {
    let popupCardBoardId = Session.get('popupCardBoardId');
    let currentBoard = Session.get('currentBoard');
    let ret = currentBoard;
    if (popupCardBoardId) {
      ret = popupCardBoardId;
    }
    return ret;
  },
  getCurrentCardId(ignorePopupCard) {
    let ret = Session.get('currentCard');
    if (!ret && !ignorePopupCard) {
      ret = Utils.getPopupCardId();
    }
    return ret;
  },
  getPopupCardId() {
    const ret = Session.get('popupCardId');
    return ret;
  },
  getCurrentListId() {
    const ret = Session.get('currentList');
    return ret;
  },
  /** returns the current board
   * <li> returns the current board or the board of the popup card if set
   */
  getCurrentBoard() {
    const boardId = Utils.getCurrentBoardId();
    const ret = ReactiveCache.getBoard(boardId);
    return ret;
  },
  getCurrentCard(ignorePopupCard) {
    const cardId = Utils.getCurrentCardId(ignorePopupCard);
    const ret = ReactiveCache.getCard(cardId);
    return ret;
  },

  // Zoom and mobile mode utilities
  getZoomLevel() {
    const user = ReactiveCache.getCurrentUser();
    if (user && user.profile && user.profile.zoomLevel !== undefined) {
      return user.profile.zoomLevel;
    }
    // For non-logged-in users, check localStorage
    const stored = localStorage.getItem('wekan-zoom-level');
    return stored ? parseFloat(stored) : 1.0;
  },

  setZoomLevel(level) {
    const user = ReactiveCache.getCurrentUser();
    if (user) {
      // Update user profile
      user.setZoomLevel(level);
    } else {
      // Store in localStorage for non-logged-in users
      localStorage.setItem('wekan-zoom-level', level.toString());
    }
    Utils.applyZoomLevel(level);

    // Trigger reactive updates for UI components
    Session.set('wekan-zoom-level', level);
  },

  getMobileMode() {
    const user = ReactiveCache.getCurrentUser();
    if (user && user.profile && user.profile.mobileMode !== undefined) {
      return user.profile.mobileMode;
    }
    // For non-logged-in users, check localStorage
    const stored = localStorage.getItem('wekan-mobile-mode');
    return stored ? stored === 'true' : false;
  },

  setMobileMode(enabled) {
    const user = ReactiveCache.getCurrentUser();
    if (user) {
      // Update user profile
      user.setMobileMode(enabled);
    } else {
      // Store in localStorage for non-logged-in users
      localStorage.setItem('wekan-mobile-mode', enabled.toString());
    }
    Utils.applyMobileMode(enabled);
    // Trigger reactive updates for UI components
    Session.set('wekan-mobile-mode', enabled);
  },

  applyZoomLevel(level) {
    const boardWrapper = document.querySelector('.board-wrapper');
    const body = document.body;
    const isMobileMode = body.classList.contains('mobile-mode');

    if (boardWrapper) {
      if (isMobileMode) {
        // On mobile mode, only apply zoom to text and icons, not the entire layout
        // Remove any existing transform from board-wrapper
        boardWrapper.style.transform = '';
        boardWrapper.style.transformOrigin = '';

        // Apply zoom to text and icon elements instead
        const textElements = boardWrapper.querySelectorAll('h1, h2, h3, h4, h5, h6, p, span, div, .minicard, .list-header-name, .board-header-btn, .fa, .icon');
        textElements.forEach(element => {
          element.style.transform = `scale(${level})`;
          element.style.transformOrigin = 'center';
        });

        // Reset board-canvas height
        const boardCanvas = document.querySelector('.board-canvas');
        if (boardCanvas) {
          boardCanvas.style.height = '';
        }
      } else {
        // Desktop mode: apply zoom to entire board-wrapper as before
        boardWrapper.style.transform = `scale(${level})`;
        boardWrapper.style.transformOrigin = 'top left';

        // If zoom is 50% or lower, make board wrapper full width like content
        if (level <= 0.5) {
          boardWrapper.style.width = '100%';
          boardWrapper.style.maxWidth = '100%';
          boardWrapper.style.margin = '0';
        } else {
          // Reset to normal width for higher zoom levels
          boardWrapper.style.width = '';
          boardWrapper.style.maxWidth = '';
          boardWrapper.style.margin = '';
        }

        // Adjust container height to prevent scroll issues
        const boardCanvas = document.querySelector('.board-canvas');
        if (boardCanvas) {
          boardCanvas.style.height = `${100 / level}%`;

          // For high zoom levels (200%+), enable both horizontal and vertical scrolling
          if (level >= 2.0) {
            boardCanvas.style.overflowX = 'auto';
            boardCanvas.style.overflowY = 'auto';
            // Ensure the content area can scroll both horizontally and vertically
            const content = document.querySelector('#content');
            if (content) {
              content.style.overflowX = 'auto';
              content.style.overflowY = 'auto';
            }
          } else {
            // Reset overflow for normal zoom levels
            boardCanvas.style.overflowX = '';
            boardCanvas.style.overflowY = '';
            const content = document.querySelector('#content');
            if (content) {
              content.style.overflowX = '';
              content.style.overflowY = '';
            }
          }
        }
      }
    }
  },

  applyMobileMode(enabled) {
    const body = document.body;
    if (enabled) {
      body.classList.add('mobile-mode');
      body.classList.remove('desktop-mode');
    } else {
      body.classList.add('desktop-mode');
      body.classList.remove('mobile-mode');
    }
  },

  initializeUserSettings() {
    // Apply saved settings on page load
    const zoomLevel = Utils.getZoomLevel();
    const mobileMode = Utils.getMobileMode();
    Utils.applyZoomLevel(zoomLevel);
    Utils.applyMobileMode(mobileMode);
  },
  getCurrentList() {
    const listId = this.getCurrentListId();
    let ret = null;
    if (listId) {
      ret = ReactiveCache.getList(listId);
    }
    return ret;
  },
  getPopupCard() {
    const cardId = Utils.getPopupCardId();
    const ret = ReactiveCache.getCard(cardId);
    return ret;
  },
  canModifyCard() {
    const currentUser = ReactiveCache.getCurrentUser();
    const ret = (
      currentUser &&
      currentUser.isBoardMember() &&
      !currentUser.isCommentOnly() &&
      !currentUser.isWorker()
    );
    return ret;
  },
  canModifyBoard() {
    const currentUser = ReactiveCache.getCurrentUser();
    const ret = (
      currentUser &&
      currentUser.isBoardMember() &&
      !currentUser.isCommentOnly()
    );
    return ret;
  },
  reload() {
    // we move all window.location.reload calls into this function
    // so we can disable it when running tests.
    // This is because we are not allowed to override location.reload but
    // we can override Utils.reload to prevent reload during tests.
    window.location.reload();
  },
  setBoardView(view) {
    currentUser = ReactiveCache.getCurrentUser();
    if (currentUser) {
      ReactiveCache.getCurrentUser().setBoardView(view);
    } else if (view === 'board-view-swimlanes') {
      window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
      Utils.reload();
    } else if (view === 'board-view-lists') {
      window.localStorage.setItem('boardView', 'board-view-lists'); //true
      Utils.reload();
    } else if (view === 'board-view-cal') {
      window.localStorage.setItem('boardView', 'board-view-cal'); //true
      Utils.reload();
    } else {
      window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
      Utils.reload();
    }
  },

  unsetBoardView() {
    window.localStorage.removeItem('boardView');
    window.localStorage.removeItem('collapseSwimlane');
  },

  boardView() {
    currentUser = ReactiveCache.getCurrentUser();
    if (currentUser) {
      return (currentUser.profile || {}).boardView;
    } else if (
      window.localStorage.getItem('boardView') === 'board-view-swimlanes'
    ) {
      return 'board-view-swimlanes';
    } else if (
      window.localStorage.getItem('boardView') === 'board-view-lists'
    ) {
      return 'board-view-lists';
    } else if (window.localStorage.getItem('boardView') === 'board-view-cal') {
      return 'board-view-cal';
    } else {
      window.localStorage.setItem('boardView', 'board-view-swimlanes'); //true
      Utils.reload();
      return 'board-view-swimlanes';
    }
  },

  myCardsSort() {
    let sort = window.localStorage.getItem('myCardsSort');

    if (!sort || !['board', 'dueAt'].includes(sort)) {
      sort = 'board';
    }

    return sort;
  },

  myCardsSortToggle() {
    if (this.myCardsSort() === 'board') {
      this.setMyCardsSort('dueAt');
    } else {
      this.setMyCardsSort('board');
    }
  },

  setMyCardsSort(sort) {
    window.localStorage.setItem('myCardsSort', sort);
    Utils.reload();
  },

  archivedBoardIds() {
    const ret = ReactiveCache.getBoards({ archived: false }).map(board => board._id);
    return ret;
  },

  dueCardsView() {
    let view = window.localStorage.getItem('dueCardsView');

    if (!view || !['me', 'all'].includes(view)) {
      view = 'me';
    }

    return view;
  },

  setDueCardsView(view) {
    window.localStorage.setItem('dueCardsView', view);
    Utils.reload();
  },

  myCardsView() {
    let view = window.localStorage.getItem('myCardsView');

    if (!view || !['boards', 'table'].includes(view)) {
      view = 'boards';
    }

    return view;
  },

  setMyCardsView(view) {
    window.localStorage.setItem('myCardsView', view);
    Utils.reload();
  },

  // XXX We should remove these two methods
  goBoardId(_id) {
    const board = ReactiveCache.getBoard(_id);
    return (
      board &&
      FlowRouter.go('board', {
        id: board._id,
        slug: board.slug,
      })
    );
  },

  goCardId(_id) {
    const card = ReactiveCache.getCard(_id);
    const board = ReactiveCache.getBoard(card.boardId);
    return (
      board &&
      FlowRouter.go('card', {
        cardId: card._id,
        boardId: board._id,
        slug: board.slug,
      })
    );
  },
  getCommonAttachmentMetaFrom(card) {
    const meta = {};
    if (card.isLinkedCard()) {
      meta.boardId = ReactiveCache.getCard(card.linkedId).boardId;
      meta.cardId = card.linkedId;
    } else {
      meta.boardId = card.boardId;
      meta.swimlaneId = card.swimlaneId;
      meta.listId = card.listId;
      meta.cardId = card._id;
    }
    return meta;
  },
  MAX_IMAGE_PIXEL: Meteor.settings.public.MAX_IMAGE_PIXEL,
  COMPRESS_RATIO: Meteor.settings.public.IMAGE_COMPRESS_RATIO,
  shrinkImage(options) {
    // shrink image to certain size
    const dataurl = options.dataurl,
      callback = options.callback,
      toBlob = options.toBlob;
    let canvas = document.createElement('canvas'),
      image = document.createElement('img');
    const maxSize = options.maxSize || 1024;
    const ratio = options.ratio || 1.0;
    const next = function (result) {
      image = null;
      canvas = null;
      if (typeof callback === 'function') {
        callback(result);
      }
    };
    image.onload = function () {
      let width = this.width,
        height = this.height;
      let changed = false;
      if (width > height) {
        if (width > maxSize) {
          height *= maxSize / width;
          width = maxSize;
          changed = true;
        }
      } else if (height > maxSize) {
        width *= maxSize / height;
        height = maxSize;
        changed = true;
      }
      canvas.width = width;
      canvas.height = height;
      canvas.getContext('2d').drawImage(this, 0, 0, width, height);
      if (changed === true) {
        const type = 'image/jpeg';
        if (toBlob) {
          canvas.toBlob(next, type, ratio);
        } else {
          next(canvas.toDataURL(type, ratio));
        }
      } else {
        next(changed);
      }
    };
    image.onerror = function () {
      next(false);
    };
    image.src = dataurl;
  },
  capitalize(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
  },

  windowResizeDep: new Tracker.Dependency(),
  // in fact, what we really care is screen size
  // large mobile device like iPad or android Pad has a big screen, it should also behave like a desktop
  // in a small window (even on desktop), Wekan run in compact mode.
  // we can easily debug with a small window of desktop browser. :-)
  isMiniScreen() {
    this.windowResizeDep.depend();
    // Also depend on mobile mode changes to make this reactive
    Session.get('wekan-mobile-mode');
    
    // Show mobile view when:
    // 1. Screen width is 800px or less (matches CSS media queries)
    // 2. Mobile phones in portrait mode
    // 3. iPad in very small screens (≤ 600px)
    // 4. All iPhone models by default (including largest models), but respect user preference
    const isSmallScreen = window.innerWidth <= 800;
    const isVerySmallScreen = window.innerWidth <= 600;
    const isPortrait = window.innerWidth < window.innerHeight || window.matchMedia("(orientation: portrait)").matches;
    const isMobilePhone = /Mobile|Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) && !/iPad/i.test(navigator.userAgent);
    const isIPhone = /iPhone|iPod/i.test(navigator.userAgent);
    const isIPad = /iPad/i.test(navigator.userAgent);
    const isUbuntuTouch = /Ubuntu/i.test(navigator.userAgent);

    // Check if user has explicitly set mobile mode preference
    const userMobileMode = this.getMobileMode();
    
    // For iPhone: default to mobile view, but respect user's mobile mode toggle preference
    // This ensures all iPhone models (including iPhone 15 Pro Max, 14 Pro Max, etc.) start with mobile view
    // but users can still switch to desktop mode if they prefer
    if (isIPhone) {
      // If user has explicitly set a preference, respect it
      if (userMobileMode !== null && userMobileMode !== undefined) {
        return userMobileMode;
      }
      // Otherwise, default to mobile view for iPhones
      return true;
    } else if (isMobilePhone) {
      return isPortrait; // Other mobile phones: portrait = mobile, landscape = desktop
    } else if (isIPad) {
      return isVerySmallScreen; // iPad: only very small screens get mobile view
    } else if (isUbuntuTouch) {
      // Ubuntu Touch: smartphones (≤ 600px) behave like mobile phones, tablets (> 600px) like iPad
      if (isVerySmallScreen) {
        return isPortrait; // Ubuntu Touch smartphone: portrait = mobile, landscape = desktop
      } else {
        return isVerySmallScreen; // Ubuntu Touch tablet: only very small screens get mobile view
      }
    } else {
      return isSmallScreen; // Desktop: based on 800px screen width
    }
  },

  isTouchScreen() {
    // NEW TOUCH DEVICE DETECTION:
    // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
    var hasTouchScreen = false;
    if ("maxTouchPoints" in navigator) {
      hasTouchScreen = navigator.maxTouchPoints > 0;
    } else if ("msMaxTouchPoints" in navigator) {
      hasTouchScreen = navigator.msMaxTouchPoints > 0;
    } else {
      var mQ = window.matchMedia && matchMedia("(pointer:coarse)");
      if (mQ && mQ.media === "(pointer:coarse)") {
        hasTouchScreen = !!mQ.matches;
      } else if ('orientation' in window) {
        hasTouchScreen = true; // deprecated, but good fallback
      } else {
        // Only as a last resort, fall back to user agent sniffing
        var UA = navigator.userAgent;
        hasTouchScreen = (
          /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
          /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA)
        );
      }
    }
    return hasTouchScreen;
  },

  // returns if desktop drag handles are enabled
  isShowDesktopDragHandles() {
    // Always show drag handles on all displays
    return true;
  },

  // returns if mini screen or desktop drag handles
  isTouchScreenOrShowDesktopDragHandles() {
    // Always enable drag handles for all displays
    return true;
  },

  calculateIndexData(prevData, nextData, nItems = 1) {
    let base, increment;
    // If we drop the card to an empty column
    if (!prevData && !nextData) {
      base = 0;
      increment = 1;
      // If we drop the card in the first position
    } else if (!prevData) {
      const nextSortIndex = nextData.sort;
      const ceil = Math.ceil(nextSortIndex - 1);
      if (ceil < nextSortIndex) {
        increment = nextSortIndex - ceil;
        base = nextSortIndex - increment;
      } else {
        base = nextData.sort - 1;
        increment = -1;
      }
      // If we drop the card in the last position
    } else if (!nextData) {
      const prevSortIndex = prevData.sort;
      const floor = Math.floor(prevSortIndex + 1);
      if (floor > prevSortIndex) {
        increment = prevSortIndex - floor;
        base = prevSortIndex - increment;
      } else {
        base = prevData.sort + 1;
        increment = 1;
      }
    }
    // In the general case take the average of the previous and next element
    // sort indexes.
    else {
      const prevSortIndex = prevData.sort;
      const nextSortIndex = nextData.sort;
      if (nItems == 1 ) {
        if (prevSortIndex < 0 ) {
          const ceil = Math.ceil(nextSortIndex - 1);
          if (ceil < nextSortIndex && ceil > prevSortIndex) {
            increment = ceil - prevSortIndex;
          }
        } else {
          const floor = Math.floor(nextSortIndex - 1);
          if (floor < nextSortIndex && floor > prevSortIndex) {
            increment = floor - prevSortIndex;
          }
        }
      }
      if (!increment) {
        increment = (nextSortIndex - prevSortIndex) / (nItems + 1);
      }
      if (!base) {
        base = prevSortIndex + increment;
      }
    }
    // XXX Return a generator that yield values instead of a base with a
    // increment number.
    return {
      base,
      increment,
    };
  },

  // Determine the new sort index
  calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) {
    let prevData = null;
    let nextData = null;
    if (prevCardDomElement) {
      prevData = Blaze.getData(prevCardDomElement)
    }
    if (nextCardDomElement) {
      nextData = Blaze.getData(nextCardDomElement);
    }
    const ret = Utils.calculateIndexData(prevData, nextData, nCards);
    return ret;
  },

  manageCustomUI() {
    Meteor.call('getCustomUI', (err, data) => {
      if (err && err.error[0] === 'var-not-exist') {
        Session.set('customUI', false); // siteId || address server not defined
      }
      if (!err) {
        Utils.setCustomUI(data);
      }
    });
  },

  setCustomUI(data) {
    const currentBoard = Utils.getCurrentBoard();
    if (currentBoard) {
      DocHead.setTitle(`${currentBoard.title} - ${data.productName}`);
    } else {
      DocHead.setTitle(`${data.productName}`);
    }
  },

  setMatomo(data) {
    window._paq = window._paq || [];
    window._paq.push(['setDoNotTrack', data.doNotTrack]);
    if (data.withUserName) {
      window._paq.push(['setUserId', ReactiveCache.getCurrentUser().username]);
    }
    window._paq.push(['trackPageView']);
    window._paq.push(['enableLinkTracking']);

    (function () {
      window._paq.push(['setTrackerUrl', `${data.address}piwik.php`]);
      window._paq.push(['setSiteId', data.siteId]);

      const script = document.createElement('script');
      Object.assign(script, {
        id: 'scriptMatomo',
        type: 'text/javascript',
        async: 'true',
        defer: 'true',
        src: `${data.address}piwik.js`,
      });

      const s = document.getElementsByTagName('script')[0];
      s.parentNode.insertBefore(script, s);
    })();

    Session.set('matomo', true);
  },

  manageMatomo() {
    const matomo = Session.get('matomo');
    if (matomo === undefined) {
      Meteor.call('getMatomoConf', (err, data) => {
        if (err && err.error[0] === 'var-not-exist') {
          Session.set('matomo', false); // siteId || address server not defined
        }
        if (!err) {
          Utils.setMatomo(data);
        }
      });
    } else if (matomo) {
      window._paq.push(['trackPageView']);
    }
  },

  getTriggerActionDesc(event, tempInstance) {
    const jqueryEl = tempInstance.$(event.currentTarget.parentNode);
    const triggerEls = jqueryEl.find('.trigger-content').children();
    let finalString = '';
    for (let i = 0; i < triggerEls.length; i++) {
      const element = tempInstance.$(triggerEls[i]);
      if (element.hasClass('trigger-text')) {
        finalString += element.text().toLowerCase();
      } else if (element.hasClass('user-details')) {
        let username = element.find('input').val();
        if (username === undefined || username === '') {
          username = '*';
        }
        finalString += `${element
          .find('.trigger-text')
          .text()
          .toLowerCase()} ${username}`;
      } else if (element.find('select').length > 0) {
        finalString += element
          .find('select option:selected')
          .text()
          .toLowerCase();
      } else if (element.find('input').length > 0) {
        let inputvalue = element.find('input').val();
        if (inputvalue === undefined || inputvalue === '') {
          inputvalue = '*';
        }
        finalString += inputvalue;
      }
      // Add space
      if (i !== length - 1) {
        finalString += ' ';
      }
    }
    return finalString;
  },

  fallbackCopyTextToClipboard(text) {
    var textArea = document.createElement("textarea");
    textArea.value = text;

    // Avoid scrolling to bottom
    textArea.style.top = "0";
    textArea.style.left = "0";
    textArea.style.position = "fixed";

    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    try {
      document.execCommand('copy');
      return Promise.resolve(true);
    } catch (e) {
      return Promise.reject(false);
    } finally {
      document.body.removeChild(textArea);
    }
  },

  /** copy the text to the clipboard
   * @see https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript/30810322#30810322
   * @param string copy this text to the clipboard
   * @return Promise
   */
  copyTextToClipboard(text) {
    let ret;
    if (navigator.clipboard) {
      ret = navigator.clipboard.writeText(text).then(function () {
      }, function (err) {
        console.error('Async: Could not copy text: ', err);
      });
    } else {
      ret = Utils.fallbackCopyTextToClipboard(text);
    }
    return ret;
  },

  /** show the "copied!" message
   * @param promise the promise of Utils.copyTextToClipboard
   * @param $tooltip jQuery tooltip element
   */
  showCopied(promise, $tooltip) {
    if (promise) {
      promise.then(() => {
        $tooltip.show(100);
        setTimeout(() => $tooltip.hide(100), 1000);
      }, (err) => {
        console.error("error: ", err);
      });
    }
  },
};

// A simple tracker dependency that we invalidate every time the window is
// resized. This is used to reactively re-calculate the popup position in case
// of a window resize. This is the equivalent of a "Signal" in some other
// programming environments (eg, elm).
$(window).on('resize', () => Utils.windowResizeDep.changed());
