const express = require('express');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const moment = require('moment-timezone');
const puppeteer = require('puppeteer');
const cron = require('node-cron');

const app = express();
const PORT = 3005;
const JSON_DIR = path.join(__dirname, 'json');

// Ensure JSON directory exists
if (!fs.existsSync(JSON_DIR)) {
    fs.mkdirSync(JSON_DIR);
}

const USERNAME = 'Swight';
const USER_ID = 6405416;

// Function to fetch AniList stats
async function fetchAnilistStats() {
    const query = `
    query ($userId: Int) {
        User(id: $userId) {
            statistics {
                anime {
                    count
                    meanScore
                    minutesWatched
                    episodesWatched
                }
                manga {
                    count
                    meanScore
                    chaptersRead
                    volumesRead
                }
            }
        }
    }
    `;

    const variables = { userId: USER_ID };

    try {
        const response = await axios.post('https://graphql.anilist.co', { query, variables });
        return response.data;
    } catch (error) {
        console.error('Error fetching AniList stats:', error.response ? error.response.data : error.message);
        throw error;
    }
}

// Function to fetch AniList activities
async function fetchActivities() {
    const activityQuery = `
    query ($userId: Int!, $page: Int!) {
        Page(page: $page, perPage: 50) {
            pageInfo {
                currentPage
                lastPage
            }
            activities(userId: $userId) {
                ... on ListActivity {
                    status
                    progress
                    createdAt
                    media {
                        title {
                            english
                            romaji
                        }
                        coverImage {
                            large
                        }
                        siteUrl
                    }
                }
                ... on TextActivity {
                    createdAt
                    text
                }
            }
        }
    }
    `;

    let currentPage = 1;
    let lastPage = 1;
    const activities = [];

    do {
        try {
            const response = await axios.post('https://graphql.anilist.co', {
                query: activityQuery,
                variables: { userId: USER_ID, page: currentPage },
            });

            const data = response.data.data;
            activities.push(...data.Page.activities);
            currentPage = data.Page.pageInfo.currentPage + 1;
            lastPage = data.Page.pageInfo.lastPage;
        } catch (error) {
            console.error('Error fetching activities:', error.response ? error.response.data : error.message);
            throw error;
        }
    } while (currentPage <= lastPage);

    // Group activities by date
    const groupedActivities = activities.reduce((acc, activity) => {
        // Convert createdAt timestamp to a date string (YYYY-MM-DD)
        const date = new Date(activity.createdAt * 1000).toISOString().split('T')[0];

        // Initialize the date key if it doesn't exist
        if (!acc[date]) {
            acc[date] = [];
        }

        // Add the activity to the corresponding date
        acc[date].push(activity);

        return acc;
    }, {});

    return groupedActivities;
}

// Function to fetch AniList favorites
async function fetchFavorites() {
    const favoritesQuery = `
    query ($userId: Int!) {
        User(id: $userId) {
            favourites {
                anime {
                    nodes {
                        id
                        title {
                            romaji
                            english
                        }
                        coverImage {
                            large
                        }
                        siteUrl
                    }
                }
                manga {
                    nodes {
                        id
                        title {
                            romaji
                            english
                        }
                        coverImage {
                            large
                        }
                        siteUrl
                    }
                }
                characters {
                    nodes {
                        id
                        name {
                            full
                            native
                        }
                        image {
                            large
                        }
                        siteUrl
                    }
                }
                staff {
                    nodes {
                        id
                        name {
                            full
                            native
                        }
                        image {
                            large
                        }
                        siteUrl
                    }
                }
            }
        }
    }
    `;

    try {
        const response = await axios.post('https://graphql.anilist.co', {
            query: favoritesQuery,
            variables: { userId: USER_ID },
        });

        return response.data.data.User.favourites;
    } catch (error) {
        console.error('Error fetching favorites:', error.response ? error.response.data : error.message);
        throw error;
    }
}

// Function to fetch AniList genre overview
async function fetchGenreOverview() {
    const genreOverviewQuery = `
    query ($userId: Int!) {
        User(id: $userId) {
            statistics {
                anime {
                    genres {
                        genre
                        count
                        meanScore
                        minutesWatched
                    }
                }
                manga {
                    genres {
                        genre
                        count
                        meanScore
                        chaptersRead
                    }
                }
            }
        }
    }
    `;

    try {
        const response = await axios.post('https://graphql.anilist.co', {
            query: genreOverviewQuery,
            variables: { userId: USER_ID },
        });

        return response.data.data.User.statistics;
    } catch (error) {
        console.error('Error fetching genre overview:', error.response ? error.response.data : error.message);
        throw error;
    }
}

// Function to fetch AniList anime list
async function fetchUserAnimeList() {
    const query = `
    query ($userName: String) {
        MediaListCollection(userName: $userName, type: ANIME) {
            lists {
                name
                entries {
                    media {
                        id
                        title {
                            english
                            romaji
                        }
                        coverImage {
                            medium
                        }
                        averageScore
                        episodes
                        status
                        genres
                        format
                            countryOfOrigin
                    }
                    score
                    progress
                }
            }
        }
    }
    `;

    const variables = { userName: USERNAME };

    try {
        const response = await axios.post('https://graphql.anilist.co', { query, variables });
        const lists = response.data.data.MediaListCollection.lists;
        const animeList = lists.map(list => ({
            name: list.name,
            entries: list.entries.map(entry => ({
                id: entry.media.id,
                title: entry.media.title.english || entry.media.title.romaji,
                coverImage: entry.media.coverImage.medium,
                averageScore: entry.media.averageScore,
                episodes: entry.media.episodes,
                status: entry.media.status,
                genres: entry.media.genres,
                format: entry.media.format,
                    country: entry.media.countryOfOrigin,
                    personalScore: entry.score,
                    progress: entry.progress,
                    links: {
                        anilist: `https://anilist.co/anime/${entry.media.id}/`,
                        mal: `https://myanimelist.net/anime/${entry.media.id}`,
                        kitsu: `https://kitsu.io/anime/${entry.media.id}`
                    }
            })).sort((a, b) => b.personalScore - a.personalScore)
        }));

        return animeList;
    } catch (error) {
        console.error('Error fetching user anime list:', error);
        throw error;
    }
}

// Function to fetch AniList manga list
async function fetchUserMangaList() {
    const query = `
    query ($userName: String) {
        MediaListCollection(userName: $userName, type: MANGA) {
            lists {
                name
                entries {
                    media {
                        id
                        title {
                            english
                            romaji
                        }
                        coverImage {
                            medium
                        }
                        averageScore
                        chapters
                        status
                        genres
                        format
                            countryOfOrigin
                    }
                    score
                    progress
                }
            }
        }
    }
    `;

    const variables = { userName: USERNAME };

    try {
        const response = await axios.post('https://graphql.anilist.co', { query, variables });
        const lists = response.data.data.MediaListCollection.lists;
        const mangaList = lists.map(list => ({
            name: list.name,
            entries: list.entries.map(entry => ({
                id: entry.media.id,
                title: entry.media.title.english || entry.media.title.romaji,
                coverImage: entry.media.coverImage.medium,
                averageScore: entry.media.averageScore,
                chapters: entry.media.chapters,
                status: entry.media.status,
                genres: entry.media.genres,
                format: entry.media.format,
                    country: entry.media.countryOfOrigin,
                    personalScore: entry.score,
                    progress: entry.progress,
                    links: {
                        anilist: `https://anilist.co/manga/${entry.media.id}/`,
                        mal: `https://myanimelist.net/manga/${entry.media.id}`,
                        kitsu: `https://kitsu.io/manga/${entry.media.id}`
                    }
            })).sort((a, b) => b.personalScore - a.personalScore)
        }));

        return mangaList;
    } catch (error) {
        console.error('Error fetching user manga list:', error);
        throw error;
    }
}

// Function to fetch StoryGraph list
async function fetchUserStoryGraphList() {
    async function autoScroll(page) {
        await page.evaluate(async () => {
            await new Promise((resolve) => {
                let totalHeight = 0;
                const distance = 100;
                const timer = setInterval(() => {
                    const scrollHeight = document.body.scrollHeight;
                    window.scrollBy(0, distance);
                    totalHeight += distance;

                    if (totalHeight >= scrollHeight) {
                        clearInterval(timer);
                        resolve();
                    }
                }, 100);
            });
        });
    }

    async function getPublicationDateFromOpenLibrary(title, author) {
        try {
            const searchUrl = `https://openlibrary.org/search.json?title=${encodeURIComponent(title)}&author=${encodeURIComponent(author)}`;
            const response = await axios.get(searchUrl);
            const book = response.data.docs[0];
            if (book) {
                const publicationDate = book.first_publish_year || 'N/A';
                return publicationDate;
            }
            return 'N/A';
        } catch (error) {
            console.error(`Error fetching details for ${title} by ${author}:`, error);
            return 'N/A';
        }
    }

    async function getPublicationDateFromAudible(title, author) {
        try {
            const searchUrl = `https://www.audible.com/search?keywords=${encodeURIComponent(title + ' ' + author)}`;
            const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
            const page = await browser.newPage();
            await page.goto(searchUrl, { waitUntil: 'networkidle2', timeout: 60000 });

            const publicationDate = await page.evaluate(() => {
                const dateElement = document.querySelector('.releaseDateLabel span');
                if (dateElement) {
                    const dateText = dateElement.textContent.trim();
                    const yearMatch = dateText.match(/\d{2}-\d{2}-(\d{2})/);
                    if (yearMatch) {
                        return `20${yearMatch[1]}`; // Assuming 20xx for the year
                    }
                }
                return 'N/A';
            });

            await browser.close();
            return publicationDate;
        } catch (error) {
            console.error(`Error fetching details for ${title} by ${author} from Audible:`, error);
            return 'N/A';
        }
    }

    async function scrapePage(url, status) {
        const browser = await puppeteer.launch({
            headless: true,
            args: ['--no-sandbox', '--disable-setuid-sandbox'],
            timeout: 60000
        });
        const page = await browser.newPage();
        page.setDefaultNavigationTimeout(60000);
        await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });

        // Scroll down to load more content
        await autoScroll(page);

        // Wait for the book panes to load
        await page.waitForSelector('.book-pane', { timeout: 60000 });

        const books = await page.evaluate((status) => {
            return Array.from(document.querySelectorAll('.book-pane')).map(book => {
                const pagesText = book.querySelector('.text-xs').innerText.split(' • ')[0];
                let format = 'N/A';
                if (pagesText.includes('pages')) {
                    format = 'Book';
                } else if (pagesText.includes('minutes')) {
                    format = 'Audiobook';
                }

                return {
                    title: book.querySelector('.book-title-author-and-series a').innerText,
                                                                           author: book.querySelector('.book-title-author-and-series a[href^="/authors"]').innerText,
                                                                           coverImage: book.querySelector('.book-cover img').src,
                                                                           pages: pagesText,
                                                                           status: status,
                                                                           genres: Array.from(book.querySelectorAll('.book-pane-tag-section .inline-block')).map(tag => tag.innerText),
                                                                           format: format,
                };
            });
        }, status);

        for (const book of books) {
            if (book.format === 'Audiobook') {
                book.publicationDate = await getPublicationDateFromAudible(book.title, book.author);
            } else {
                book.publicationDate = await getPublicationDateFromOpenLibrary(book.title, book.author);
            }
            console.log(`Fetched details for ${book.title}: Publication Date - ${book.publicationDate}`);
        }

        await browser.close();
        return books;
    }

    try {
        const currentlyReading = await scrapePage('https://app.thestorygraph.com/currently-reading/swight', 'Currently reading');
        const readRecently = await scrapePage('https://app.thestorygraph.com/books-read/swight', 'Read recently');
        const toRead = await scrapePage('https://app.thestorygraph.com/to-read/swight', 'To read');

        const books = [...currentlyReading, ...readRecently, ...toRead];
        console.log(`Total books found: ${books.length}`);

        if (books.length === 0) {
            throw new Error('No books found. Scraping might have failed.');
        }

        return books;
    } catch (error) {
        console.error('Error fetching user StoryGraph list:', error);
        throw error;
    }
}

// Function to generate StoryGraph stats
function generateStorygraphStats(books) {
    let totalBooksRead = 0;
    let totalPagesRead = 0;
    let totalMinutesListened = 0;

    const timeToMinutes = (timeString) => {
        const [hours, minutes] = timeString.split(' hours, ').map(str => parseInt(str, 10));
        return (hours * 60) + minutes;
    };

    books.forEach(book => {
        if (book.status === 'Read recently') {
            totalBooksRead++;

            if (book.format === 'Book') {
                const pages = parseInt(book.pages.split(' ')[0], 10);
                totalPagesRead += pages;
            } else if (book.format === 'Audiobook') {
                const minutes = timeToMinutes(book.pages);
                totalMinutesListened += minutes;
            }
        }
    });

    return {
        totalBooksRead,
        totalPagesRead,
        totalMinutesListened
    };
}

// Function to save data to JSON file
function saveToFile(filename, data) {
    const filePath = path.join(JSON_DIR, filename);
    fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
    console.log(`Data saved to ${filePath}`);
}

// Function to fetch all data except StoryGraph and save to JSON files
async function fetchAllDataExceptStoryGraph() {
    try {
        const stats = await fetchAnilistStats();
        saveToFile('stats.json', stats);

        const activities = await fetchActivities();
        saveToFile('activityHistory.json', activities);

        const favorites = await fetchFavorites();
        saveToFile('favorites.json', favorites);

        const genreOverview = await fetchGenreOverview();
        saveToFile('genreOverview.json', genreOverview);

        const animeList = await fetchUserAnimeList();
        saveToFile('anime_list.json', animeList);

        const mangaList = await fetchUserMangaList();
        saveToFile('manga_list.json', mangaList);
    } catch (error) {
        console.error('Error fetching data:', error);
    }
}

// Function to fetch StoryGraph data and save to JSON files
async function fetchStoryGraphData() {
    try {
        const storygraphList = await fetchUserStoryGraphList();
        saveToFile('storygraph_list.json', storygraphList);

        const storygraphStats = generateStorygraphStats(storygraphList);
        saveToFile('storygraph_stats.json', storygraphStats);
    } catch (error) {
        console.error('Error fetching StoryGraph data:', error);
    }
}

// Schedule the task to run every 10 minutes
cron.schedule('*/10 * * * *', () => {
    console.log('Running scheduled task for AniList data...');
    fetchAllDataExceptStoryGraph();
});

// Schedule the task to run every hour
cron.schedule('0 * * * *', () => {
    console.log('Running scheduled task for StoryGraph data...');
    fetchStoryGraphData();
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
    fetchAllDataExceptStoryGraph(); // Fetch data immediately on startup
    fetchStoryGraphData(); // Fetch StoryGraph data immediately on startup
});
