/*!
 * 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:
 *   Eric Poirier <eric.poirier@eclipse-foundation.org>
 *   Olivier Goulet <olivier.goulet@eclipse-foundation.org>
 *
 * SPDX-License-Identifier: EPL-2.0
 */

import $ from 'jquery';
import Mustache from 'mustache';
import 'jquery-match-height';
import template from './templates/tpl-projects-item.mustache';
import "numeral";
import List from 'list.js';
import { getProjectsData, getProjectProposals } from "../api/eclipsefdn.projects";

const projectsAlphanumericComparator = (projectA, projectB) => {
    if (projectA.name < projectB.name) return -1;
    if (projectA.name > projectB.name) return 1;

    return 0;
}

// Shuffles an array randomly using the Array.sort() method
const shuffle = () => {
    return 0.5 - Math.random();
}

// Maps a project or project proposal into data accepted by the template
const projectMapper = (data) => {
    return {
        name: data.name,
        summary: data.summary,
        logo: data.isProposal ? '' : data.logo,
        category: data.isProposal ? 'Project Proposal' : data.category,
        website_url: data.isProposal ? data.project_url : data.website_url,
        version: data.version,
        state: data.state,
        isProposal: data.isProposal || false
    }
}

const extractWorkingGroupIdFromUrl = (url) => {
    if (!url) return;

    return new URL(url).searchParams.get('working_group');
}

const validTypes = new Set(['projects', 'proposals']);

const defaultOptions = {
    id: '',
    templateId: '',
    projectIds: '',
    url: '',
    isStaticSource: false,
    types: ['projects', 'proposals'],
    pageSize: null,
    sortingMethod: 'alphanumeric' // 'alphanumeric' or 'random'
}

const render = async () => {
    const element = document.querySelector('.featured-projects');
    if (!element) return;

    const options = { 
        ...defaultOptions, 
        ...element.dataset, 
        isStaticSource: element.dataset.isStaticSource === 'true',
        types: element.dataset.types
            ? element.dataset.types.split(',').map(t => t.trim())
            : defaultOptions.types
    };

    options.types.forEach(t => {
        if (!validTypes.has(t)) {
            console.error(`Invalid type provided to featured-projects "${t}"`);
        }
    })

    function fetchCategories() {
        let elem = document.getElementById('projects-categories');
        if (elem !== null) {
            let categoriesUrl = elem.getAttribute('data-categories');
            return fetch(categoriesUrl).then(response => response.json());
        }

        // returning an empty promise if categories were not passed.
        return async () => { };
    }

    let resultsProjects = [];
    let resultsCategories = [];
    let resultsProjectProposals = [];

    // Fetch projects
    let projectsData = [];
    if (options.types.includes('projects')) {
        projectsData = await getProjectsData(options.url, options.isStaticSource);
    }

    // Fetch project proposals. We don't want to include static source projects here.
    if (options.types.includes('proposals') && !options.isStaticSource) { 
        const workingGroupId = extractWorkingGroupIdFromUrl(options.url);
        const [proposals] = await getProjectProposals(workingGroupId);

        if (proposals) resultsProjectProposals = proposals;
    };
    

    Promise.allSettled([projectsData, fetchCategories()])
        .then(function (responses) {
            if (responses[0].status === 'fulfilled') {
                const projects = responses[0].value;
                if (projects.length > 0) {
                    resultsProjects = projects;
                }
            }

            if (responses[1].status === 'fulfilled') {
                resultsCategories = responses[1].value;
            }

            $(resultsProjects).each(function (key, project) {

                // Populate the Tags array
                if (resultsCategories.length <= 0 && typeof resultsProjects[key].tags !== "undefined") {
                    const project_tags = resultsProjects[key].tags;
                    if (project_tags.length !== 0) {
                        tags[resultsProjects[key].project_id] = project_tags;
                    }
                }
            });
            
            let projects = [
                ...resultsProjects,
                ...resultsProjectProposals.map(p => ({
                    ...p,
                    isProposal: true,
                    category: 'Project Proposal'
                }))
            ];

            projects = projects
                .sort(options.sortingMethod === 'random' ? shuffle : projectsAlphanumericComparator)
                .slice(0, options.pageSize || projects.length)
                .map(p => ({ 
                    ...p, 
                    category: getCategory(resultsCategories, p.project_id).toString() 
                }))
                .map(projectMapper);

            const data = {
                items: projects
            };

            // Render component
            let html = '';
            if (options.templateId !== '' && document.getElementById(options.templateId)) {
                const theme = document.getElementById(options.templateId).innerHTML;
                html = Mustache.render(theme, data);
            } else {
                html = template(data);
            }
            element.innerHTML += html;

            if (resultsCategories.length <= 0) {
                resultsCategories = tags;
            }

            const hasFilters = document.querySelector('.eclipsefdn-project-list-filters');
            if (hasFilters && options.pageSize) {
                console.warn('Using pagination with filters is not supported.');
            } else {
                $.each(resultsCategories, function (key, projects) {
                    $.each(projects, function (key, value) {
                        let duplicate = $('.eclipsefdn-project-list-filters').find("button:contains('" + value + "')");
                        if (duplicate.length > 0) {
                            return;
                        }
                        let button = document.createElement('button');
                        button.innerHTML = value;
                        button.className = 'btn btn-filter-project';
                        button.setAttribute('data-toggle', 'button');
                        $('.eclipsefdn-project-list-filters').append(button);
                    });
                });
            }

            const values = {
                valueNames: [
                    'name',
                    'category'
                ],
            };

            let list = new List('projects-list', values);
            
            // Recalculate heights after search or filter
            const recalculateHeights = () => {
                $('.featured-projects-item').matchHeight({ byRow: true });
            }
            list.on('filterComplete', recalculateHeights);
            list.on('searchComplete', recalculateHeights);

            $('.btn-filter-project').on('click', function (elem) {
                $('.btn-filter-project').not(this).each(function () {
                    $(this).removeClass('active');
                });
                setTimeout(function () {
                    list.filter(computeFilterFunction);
                }, 10);
            });

            $.fn.matchHeight._applyDataApi();
        })
        .catch(err => console.log(err));


    let tags = {};

    let getCategory = function (categories, project_id) {
        let category = "Other Tools";
        if (typeof categories[project_id] !== "undefined") {
            category = categories[project_id];
        }
        if (categories.length <= 0 && typeof tags[project_id] !== "undefined") {
            category = tags[project_id].toString().split(',').join(', ');
        }
        return category;
    }

    let computeFilterFunction = function (item) {
        let filter = [];
        $('.btn-filter-project').each(function (index, elem) {
            if ($(elem).hasClass('active')) {
                filter.push($(elem).text());
            }
        });
        if (filter.length == 0) {
            return true;
        }
        let found = false;
        for (let i = 0; i < filter.length; i++) {
            const element = filter[i];
            if (typeof item.values().category !== "undefined" && item.values().category.indexOf(element) !== -1) {
                found = true;
                continue;
            }
            found = false;
            break;
        }
        return found;
    };
};

const eclipsefdnProjects = {
    render
}

export default eclipsefdnProjects;
