/*!
 * Copyright (c) 2021, 2023 Eclipse Foundation, Inc.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * Contributors:
 *   Christopher Guindon <chris.guindon@eclipse-foundation.org>
 *   Zhou Fang <zhou.fang@eclipse-foundation.org>
 *   Olivier Goulet <olivier.goulet@eclipse-foundation.org>
 *
 * SPDX-License-Identifier: EPL-2.0
 */

import $ from 'jquery';
import 'jquery-match-height';
import getMembers from '../api/eclipsefdn.members';
import { displayErrMsg, scrollToAnchor, waitForRender } from './utils';
import { validateURL } from '../api/utils';
import template from './templates/explore-members/member.mustache';
import templateOnlyLogos from './templates/wg-members/wg-member-only-logos.mustache';
import templateLogoTitleWithLevels from './templates/wg-members/wg-member-logo-title-with-levels.mustache';
import templateCarousel from './templates/wg-members/carousel.mustache';
import templateLoading from './templates/loading-icon.mustache';
import { getInterestGroupParticipatingOrganizations } from '../api/eclipsefdn.interest-groups';
import { organizationMapper, getProjectParticipatingOrganizations } from '../api/eclipsefdn.membership';

const SD_NAME = 'Strategic Members';
const AP_NAME = 'Contributing Members';
const AS_NAME = 'Associate Members';

const defaultOptions = {
  mlWg: null,
  mlLevel: null,
  mlSort: null,
  mlLinkMemberWebsite: null,
  type: 'working-group',
  id: null,
};

/** A mustache "lambda" that gets called to resolve the section id. */
function getSectionId() {
  const section = this.level;
  return section
    .trim()
    .replaceAll(' ', '-')
    .toLowerCase();
}

const render = async () => {
  const elements = document.querySelectorAll('.eclipsefdn-members-list');
  
  elements.forEach(async (element) => {
    element.innerHTML = templateLoading();

    let membersListArray = [
      { level: SD_NAME, members: [] },
      { level: AP_NAME, members: [] },
      { level: AS_NAME, members: [] },
    ];
    let url = 'https://membership.eclipse.org/api/organizations?pagesize=100';

    const options = { ...defaultOptions, ...element.dataset };

    const isRandom = options.mlSort === 'random';
    const level = options.mlLevel;
    const linkMemberWebsite = element.getAttribute('data-ml-link-member-website') === 'true' ? true : false;
    
    // The id of a resource of type `options.type` (ex. working-group, interest-group, project).
    const id = options.id || options.mlWg;

    // Add the level search param if we're displaying members of the
    // Foundation. We do not use the search param for collaborations.
    if (level && !id) {
      level.split(' ').forEach((item) => (url = `${url}&levels=${item}`));
    }

    let membersData = [];
    if (options.type === 'working-group') {
      if (id) { 
        url = `${url}&working_group=${id}`;
        membersListArray = [{ level: '', members: [] }];
      }
      // When we show members belong to a wg, no need to show EF levels
      membersData = (await getMembers(url, [], (err) => displayErrMsg(element, err)))
        .map(organizationMapper);
    } else if (options.type === 'interest-group') {
      membersListArray = [{ level: '', members: [] }];

      // We assume the interest group id is a Drupal node ID if it is numeric
      // This widget needs to support node id and short ids for interest groups
      const isNodeId = !Number.isNaN(parseInt(id));
      [membersData] = await getInterestGroupParticipatingOrganizations({ 
        interestGroupId: isNodeId ? undefined : id,
        interestGroupNodeId: isNodeId ? id : undefined
      });
    } else if (options.type === 'project') {
      membersListArray = [{ level: '', members: [] }];
      [membersData] = await getProjectParticipatingOrganizations(id);
    }

    // If a level was provided, filter members of that level.
    if (level) {
      membersData = membersData.filter(createLevelFilter(level, id));
    }

    if (membersData.length === 0) {
      element.innerHTML = `<p class="members-list-info-text">No members to display.</p>`;
      return;  
    }

    const addMembersWithoutDuplicates = (levelName, memberData) => {
      // When we show members belong to a wg, only 1 item exits in membersListArray
      const currentLevelMembers = id ? membersListArray[0] : membersListArray.find((item) => item.level === levelName);
      const isDuplicatedMember = currentLevelMembers.members.find(
        (item) => item.organizationId === memberData.organizationId
      );
      !isDuplicatedMember && currentLevelMembers.members.push(memberData);
    };

    membersData = membersData.map((member) => {
      if (!member.name) {
        // will not add members without a title/name into membersListArray to display
        return member;
      }

      if (member.levels.find((item) => item.level?.toUpperCase() === 'SD')) {
        addMembersWithoutDuplicates(SD_NAME, member);
        return member;
      }

      if (member.levels.find((item) => item.level?.toUpperCase() === 'AP' || item.level?.toUpperCase() === 'OHAP')) {
        addMembersWithoutDuplicates(AP_NAME, member);
        return member;
      }

      if (member.levels.find((item) => item.level?.toUpperCase() === 'AS')) {
        addMembersWithoutDuplicates(AS_NAME, member);
        return member;
      }

      return member;
    });

    if (isRandom) {
      // Sort randomly
      membersListArray.forEach((eachLevel) => {
        let tempArray = eachLevel.members.map((item) => item);
        const randomItems = [];
        eachLevel.members.forEach(() => {
          const randomIndex = Math.floor(Math.random() * tempArray.length);
          randomItems.push(tempArray[randomIndex]);
          tempArray.splice(randomIndex, 1);
        });
        eachLevel.members = randomItems;
      });
    } else {
      // Sort alphabetically by default
      membersListArray.forEach((eachLevel) => {
        eachLevel.members.sort((a, b) => {
          const preName = a.name.toUpperCase();
          const nextName = b.name.toUpperCase();
          if (preName < nextName) {
            return -1;
          }
          if (preName > nextName) {
            return 1;
          }
          return 0;
        });
      });
    }

    if (level) {
      membersListArray = membersListArray.filter((list) => list.members.length !== 0);
    }

    const urlLinkToLogo = function () {
      if (linkMemberWebsite && validateURL(this.website)) {
        return this.website;
      }
      return `https://www.eclipse.org/membership/showMember.php?member_id=${this.organizationId}`;
    };

    const displayMembersByLevels = async (theTemplate) => {
      if (options.type !== 'working-group') {
        console.error('Only "working-group" type is supported for displaying members by level at this time');
        return;
      }
      
      const allWGData = await (await fetch('https://membership.eclipse.org/api/working_groups')).json();
      let currentWGLevels = allWGData.find((item) => item.alias === id).levels;
      // defaultLevel is for members without a valid level. So far, only occurs on IoT.
      const defaultLevel = element.getAttribute('data-ml-default-level');
      const specifiedLevel = element.getAttribute('data-ml-wg-level');
      if (specifiedLevel) {
        currentWGLevels = currentWGLevels.filter((level) =>
          specifiedLevel.toLowerCase().includes(level.relation.toLowerCase())
        );
      }
      element.innerHTML = '';
      if (defaultLevel) {
        currentWGLevels.push({ relation: 'default', description: defaultLevel, members: [] });
      }

      // categorize members into corresponding levels
      currentWGLevels.forEach((level) => {
        level['members'] = members;

        if (level.members.length === 0) {
          return;
        }

        const showLevelUnderLogo = () => {
          if (
            !element.getAttribute('data-ml-level-under-logo') ||
            element.getAttribute('data-ml-level-under-logo') === 'false'
          ) {
            return false;
          }
          return level.description.replaceAll(' Member', '');
        };

        element.innerHTML =
          element.innerHTML +
          theTemplate({ item: level.members, levelDescription: level.description, urlLinkToLogo, showLevelUnderLogo });
      });
    };

    switch (element.getAttribute('data-ml-template')) {
      case 'only-logos':
        element.innerHTML = templateOnlyLogos({
          item: membersListArray[0].members,
          urlLinkToLogo,
        });
        return;
      case 'carousel':
        element.innerHTML = templateCarousel({
          item: membersListArray[0].members,
          urlLinkToLogo,
        });
        reinitializeCarousel(element, element.dataset.count);
        return;
      case 'logo-title-with-levels':
        await displayMembersByLevels(templateLogoTitleWithLevels);
        $.fn.matchHeight._applyDataApi();
        return;
      case 'logo-with-levels':
        displayMembersByLevels(templateOnlyLogos);
        return;
      default:
        break;
    }

    // After the html renders to `element`, scroll to the level section from
    // the hash in the url if it exists.
    waitForRender(element).then(scrollToAnchor);

    element.innerHTML = template({
      sections: membersListArray,
      sectionId: getSectionId,
      hostname: window.location.hostname.includes('staging.eclipse.org')
        ? 'https://staging.eclipse.org'
        : 'https://www.eclipse.org',
    });
    $.fn.matchHeight._applyDataApi();
  });
};

export default { render };

/** Creates a filter function for filtering organizations by level. It will
  * select a different filter strategy depending on if a collaboration id was
  * provided.
  */
const createLevelFilter = (level, collaborationId) => {
  // If a collaboration ID was provided, filter organizations based on their
  // participation level with the particular industry collaboration.
  if (collaborationId) {
    return organization => organization.wgpas
      .find(wgpa => wgpa.workingGroup === collaborationId && wgpa.level === level);
  } else {
    // If no industry collaboration ID was provided, filter based on their
    // participation level with the Foundation.
    return organization => organization.levels.find(lvl => lvl.level === level);
  }
}

/**
  * Trigger the initialization of an owl carousel.
  *
  * @param {HTMLElement} element - An ancestor element of the owl carousel.
  * This is used for scoping.
  * @param {number} [count=5] - Number of carousel items to render.
  */
const reinitializeCarousel = (element, count = 9) => {
  const carouselElement = element.querySelector('.owl-carousel');
  const owl = $(carouselElement);
  if (owl.data('owl.carousel')) {
    owl.trigger('destroy.owl.carousel');
  }

  owl.owlCarousel({
    dots: true,
    items: 3,
    responsive: {
      568: {
        items: count,
      },
    }
  });
  
  carouselElement.style.display = 'block';
};
