/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

const fs = require("fs");
const https = require("https");

const teamSrc = "src/pages/team/data/team.json";
const avatarFile = "src/pages/team/data/github-avatar.json";
const avatarSize = 100;
const authorsFile = "blog/authors.json";

/**
 * Generates a random delay between min and max milliseconds
 * @param {number} min - Minimum delay in milliseconds
 * @param {number} max - Maximum delay in milliseconds
 * @returns {number} Random delay in milliseconds
 */
function getRandomDelay(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Fetches avatar image from GitHub and converts it to base64
 * @param {string} githubId - GitHub ID
 * @returns {Promise<string>} Base64 encoded avatar image
 */
function fetchAvatarAsBase64(githubId) {
    return new Promise((resolve, reject) => {
        const avatarUrl = `https://avatars.githubusercontent.com/u/${githubId}?v=4&s=${avatarSize}`;

        https
            .get(avatarUrl, (response) => {
                // Check if request was successful
                if (response.statusCode !== 200) {
                    reject(new Error(`Failed to fetch avatar from ${avatarUrl}: ${response.statusCode}`));
                    return;
                }

                const chunks = [];

                // Collect data chunks
                response.on("data", (chunk) => {
                    chunks.push(chunk);
                });

                // Convert to base64 when complete
                response.on("end", () => {
                    const buffer = Buffer.concat(chunks);
                    const base64 = buffer.toString("base64");
                    resolve(base64);
                });
            })
            .setTimeout(10000, () => {
                reject(new Error(`Failed to fetch avatar ${avatarUrl}: timed out`));
            })
            .on("error", (error) => {
                reject(error);
            });
    });
}

/**
 * Get GitHub name from URL
 * @param {string} url - GitHub URL
 * @returns {string} GitHub name
 */
function getGitName(url) {
    return url.replace('https://github.com/', '');
}

/**
 * Processes a list of githubIds and adds avatar_base64 property
 * @param {Array} ids - Array of id
 * @returns {Promise<Array>} Array of avatar_base64
 */
async function processAvatars(ids) {
    const processedArray = [];

    for (let i = 0; i < ids.length; i++) {
        const _id = ids[i];

        try {
            console.log(`-- Fetching avatar for ${_id} ... [${i + 1}/${ids.length}]`);

            // Fetch avatar and convert to base64
            const avatarBase64 = await fetchAvatarAsBase64(_id);

            processedArray.push({
                id: _id,
                avatar_base64: avatarBase64
            });
            console.log(`✓ Successfully processed ${_id}`);
        } catch (error) {
            console.error(`✗ Error processing ${_id}: ${error.message}`);
        }

        // Add random delay between 100-2000 millisecond before next request (except for the last member)
        if (i < ids.length - 1) {
            await new Promise((resolve) => setTimeout(resolve, getRandomDelay(100, 2000)));
        }
    }
    return processedArray;
}

/**
 * Processes blog authors data and adds avatar_base64 property
 * @param {Object} teamData - Team data
 * @param {Array} avatars - Array of avatars
 * @returns {Promise<Object>} Blog authors data
 */
async function processBlogAuthors(teamData, avatars) {
    const blogAuthorsMapPath = {};
    (teamData.pmc.concat(teamData.committer) || []).forEach((m) => {
        const gitName = getGitName(m.gitUrl);
        const avatarObj = avatars.find((item) => item.id === m.githubId);
        blogAuthorsMapPath[gitName] = {
            "name": m.name,
            "url": m.gitUrl,
            "image_url": "data:image/png;base64," + avatarObj.avatar_base64,
            "socials": {
                "github": gitName
            }
        }
    });

    return blogAuthorsMapPath;
}

/**
 * Main function
 */
async function main() {
    try {
        const uniqueGithubIdsSet = new Set();

        // 1. Read and parse team
        console.log(`==> Reading ${teamSrc} file`);
        const teamSrcData = JSON.parse(fs.readFileSync(teamSrc, "utf8"));

        // PMC && Committer
        (teamSrcData.pmc.concat(teamSrcData.committer) || []).forEach((d) => {
            if (d.githubId) {
                uniqueGithubIdsSet.add(d.githubId);
            }
        });

        const uniqueGithubArray = Array.from(uniqueGithubIdsSet);

        console.log("\n==> Processing avatars");
        const avatarsArray = await processAvatars(uniqueGithubArray);

        // 2. Write files
        console.log(`\n==> Write to ${avatarFile}`);
        fs.writeFileSync(avatarFile, JSON.stringify(avatarsArray, null, 2));

        // 3. Blog authors
        const blogAuthorsMapPaths = await processBlogAuthors(teamSrcData, avatarsArray);
        console.log(`\n==> Write to ${authorsFile}`);
        fs.writeFileSync(authorsFile, JSON.stringify(blogAuthorsMapPaths, null, 2));

        console.log("\n✓ Done!");
    } catch (error) {
        console.error("Error:", error.message);
        process.exit(1);
    }
}

main();