#include <iostream>
#include <curl/curl.h>
#include <getopt.h>

#include "config.hpp"

/* check version */
#ifndef VERSION
    #define VERSION "UNKNOWN"
#endif

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp){
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

/**
 * goal: ytsearch -h|-v|search-term
 *
 * @example ytsearch "rick astley"
 *          https://www.youtube.com/watch?v=dQw4w9WgXcQ
 */
int main(int argc, char **argv)
{
    if (argc < 2)
    {
        std::cout << "ytsearch: a search term is needed, try 'ytsearch -h' for more information" << std::endl;
        return EXIT_FAILURE;
    }

    int const TYPE_VIDEO    = { 0 };
    int const TYPE_CHANNEL  = { 1 };
    int const TYPE_PLAYLIST = { 2 };

    std::string const IDS[3] = {
        "WEB_PAGE_TYPE_WATCH",
        "WEB_PAGE_TYPE_CHANNEL",
        "WEB_PAGE_TYPE_PLAYLIST",
    };

    int type = TYPE_VIDEO;

    int indice_search_terms = 1;

    int opt;

    while ((opt = getopt(argc, argv, "hvcpd")) != -1)
    {
        switch (opt)
        {
            case 'h':
                std::cout << "Usage: ytsearch [options...] search-term" << std::endl <<
                " -h: show this help and quit" << std::endl <<
                " -v: show version number and quit" << std::endl <<
                " -c: get channel url" << std::endl <<
                " -d: get video url (default)" << std::endl <<
                " -p: get playlist url" << std::endl <<
                std::endl <<
                "Retrieve the url of a youtube video matching the searched terms" << std::endl;
                return EXIT_SUCCESS;
            case 'v':
                std::cout << VERSION << std::endl;
                return EXIT_SUCCESS;
            case 'c':
                type = TYPE_CHANNEL;
                indice_search_terms++;
                break;
            case 'p':
                type = TYPE_PLAYLIST;
                indice_search_terms++;
                break;
            case 'd':
                type = TYPE_VIDEO;
                indice_search_terms++;
                break;
            default:
                std::cout << "ytsearch: try 'ytsearch -h' for more information" << std::endl;
                return EXIT_FAILURE;
        }
    }

    std::string const base_search_url = { "https://www.youtube.com/results?search_query=" };
    std::string const youtube_url  = { "https://www.youtube.com/" };

    CURL *curl;
    std::string readBuffer;

    curl = curl_easy_init();

    if (!curl)
    {
        std::cout << "ytsearch: error in curl initialization" << std::endl;
        return EXIT_FAILURE;
    }

    std::string search_terms = argv[indice_search_terms];

    char* search_url_encoded = curl_easy_escape(curl, search_terms.c_str(), 0);

    std::string search_url = base_search_url + search_url_encoded;

    curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
    CURLcode res = { curl_easy_perform(curl) };
    curl_easy_cleanup(curl);
    curl_free(search_url_encoded);

    if (res != 0)
    {
        std::cout << "ytsearch: error when fetching results: " << res << std::endl;
        return EXIT_FAILURE;
    }

    std::string paths[4] = {
        "navigationEndpoint",
        "commandMetadata",
        "webCommandMetadata",
        "url",
    };

    size_t pos_start = readBuffer.find("ytInitialData");
    std::string const error_api = "ytsearch: error, youtube's api changed: update ytsearch";

    if (pos_start == std::string::npos or pos_start == 0)
    {
        std::cout << error_api << std::endl;
        return EXIT_FAILURE;
    }

    size_t first_item = readBuffer.find(IDS[type].c_str(), pos_start);

    if (first_item == std::string::npos or pos_start == 0)
    {
        std::cout << "ytsearch: error, no item found with the asked type" << std::endl;
        return EXIT_FAILURE;
    }

    while (first_item > pos_start + 60) // type is defined a little after url, we want a video
    {
        for (std::string name : paths)
        {
            pos_start = { readBuffer.find(name, pos_start) };

            if (pos_start == std::string::npos or pos_start == 0)
            {
                std::cout << error_api << std::endl;
                return EXIT_FAILURE;
            }
        }
    }

    size_t size = { pos_start + 7 }; // size of " url":"/ ", last part of paths

    size_t pos_end = { readBuffer.find('"', size) };

    std::cout << youtube_url << readBuffer.substr(size, pos_end - size) << std::endl;

    return EXIT_SUCCESS;
}