#include "email_params_rendering.h"

#include <maps/libs/locale/include/find_helpers.h>
#include <maps/libs/http/include/http.h>

namespace maps::wiki::releases_notification {

namespace {

const std::string DAY = "day";
const std::string MONTH = "month";
const std::string YEAR = "year";

std::map<locale::Locale, std::map<size_t, std::string>>
initLocalizedMonths()
{
    const std::map<size_t, std::string> tmMonthToRuName {
        {0, "января"},
        {1, "февраля"},
        {2, "марта"},
        {3, "апреля"},
        {4, "мая"},
        {5, "июня"},
        {6, "июля"},
        {7, "августа"},
        {8, "сентября"},
        {9, "октября"},
        {10, "ноября"},
        {11, "декабря"}
    };

    const std::map<size_t, std::string> tmMonthToEnName {
        {0, "January"},
        {1, "February"},
        {2, "March"},
        {3, "April"},
        {4, "May"},
        {5, "June"},
        {6, "July"},
        {7, "August"},
        {8, "September"},
        {9, "October"},
        {10, "November"},
        {11, "December"}
    };

    const std::map<size_t, std::string> tmMonthToFrName {
        {0, "Janvier"},
        {1, "Février"},
        {2, "Mars"},
        {3, "Avril"},
        {4, "Mai"},
        {5, "Juin"},
        {6, "Juillet"},
        {7, "Août"},
        {8, "Septembre"},
        {9, "Octobre"},
        {10, "Novembre"},
        {11, "Décembre"}
    };

    const std::map<size_t, std::string> tmMonthToHyName {
        {0, "Հունվար"},
        {1, "Փետրվար"},
        {2, "Մարտ"},
        {3, "Ապրիլ"},
        {4, "Մայիս"},
        {5, "Հունիս"},
        {6, "Հուլիս"},
        {7, "Օգոստոս"},
        {8, "Սեպտեմբեր"},
        {9, "Հոկտեմբեր"},
        {10, "Նոյեմբեր"},
        {11, "Դեկտեմբեր"}
    };

    const std::map<size_t, std::string> tmMonthToSrName {
        {0, "januar"},
        {1, "februar"},
        {2, "mart"},
        {3, "april"},
        {4, "maj"},
        {5, "jun"},
        {6, "jul"},
        {7, "avgust"},
        {8, "septembar"},
        {9, "oktobar"},
        {10, "novembar"},
        {11, "decembar"}
    };

    const std::map<size_t, std::string> tmMonthToTrName {
        {0, "Ocak"},
        {1, "Şubat"},
        {2, "Mart"},
        {3, "Nisan"},
        {4, "Mayıs"},
        {5, "Haziran"},
        {6, "Temmuz"},
        {7, "Ağustos"},
        {8, "Eylül"},
        {9, "Ekim"},
        {10, "Kasım"},
        {11, "Aralık"}
    };

    std::map<locale::Locale, std::map<size_t, std::string>> result;
    result.emplace(locale::Locale(locale::Lang::En), tmMonthToEnName);
    result.emplace(locale::Locale(locale::Lang::Ru), tmMonthToRuName);
    result.emplace(locale::Locale(locale::Lang::Fr), tmMonthToFrName);
    result.emplace(locale::Locale(locale::Lang::Hy), tmMonthToHyName);
    result.emplace(locale::Locale(locale::Lang::Sr), tmMonthToSrName);
    result.emplace(locale::Locale(locale::Lang::Tr), tmMonthToTrName);

    return result;
}

const std::map<locale::Locale, std::map<size_t, std::string>> localizedMonths = initLocalizedMonths();


LocaleStringMap
initLocalizedShareTitle()
{
    LocaleStringMap result;
    result.emplace(locale::Locale(locale::Lang::Ru), "Яндекс.Карты обновились.");
    return result;
}

const LocaleStringMap localizedShareTitle = initLocalizedShareTitle();

std::string
shareTitle(const locale::Locale& locale)
{
    const auto it = locale::findBest(localizedShareTitle, locale);
    ASSERT(it != localizedShareTitle.end());
    return it->second;
}


} // namespace

SimpleDate SimpleDate::fromTimePoint(chrono::TimePoint tp, const locale::Locale& locale)
{
    std::ostringstream dayStr, monthStr, yearStr;
    time_t timeTp = chrono::convertToUnixTime(tp);
    struct tm parts;
    REQUIRE(::localtime_r(&timeTp, &parts), "Cannot convert timepoint to tm struct");

    const auto locMonthsIt = locale::findBest(localizedMonths, locale);
    ASSERT(locMonthsIt != localizedMonths.end());

    dayStr << parts.tm_mday;
    monthStr << locMonthsIt->second.at(parts.tm_mon);
    yearStr << parts.tm_year + 1900;
    return {dayStr.str(), monthStr.str(), yearStr.str()};
}

void addDateParam(sender::EmailTemplateParams& params, const std::string& prefix, const SimpleDate& date)
{
    params.addParam(prefix + DAY, date.day);
    params.addParam(prefix + MONTH, date.month);
    params.addParam(prefix + YEAR, date.year);
}


namespace {

std::string makeShareDescriptionTextRu(size_t correctionsCount)
{
    std::stringstream ss;
    size_t tensNumber = (correctionsCount % 100) / 10;
    if (correctionsCount == 0) {
        ss << "А я правок не делал";
    } else if (correctionsCount == 1) {
        ss << "Моя правка опубликована";
    } else if (correctionsCount % 10 >= 2 and correctionsCount % 10 <= 4 and tensNumber != 1) {
        ss << correctionsCount << " мои правки опубликованы";
    } else if (correctionsCount % 10 == 1 and tensNumber != 1) {
        ss << correctionsCount << " моя правка опубликована";
    } else {
        ss << correctionsCount << " моих правок опубликовано";
    }
    return ss.str();
}

std::map<locale::Locale, std::function<std::string(size_t)>>
initLocalizedShareDescriptionText()
{
    std::map<locale::Locale, std::function<std::string(size_t)>> result;
    result.emplace(locale::Locale(locale::Lang::Ru), makeShareDescriptionTextRu);
    return result;
}

const std::map<locale::Locale, std::function<std::string(size_t)>>
localizedShareDescriptionText = initLocalizedShareDescriptionText();

std::string makeShareDescriptionText(size_t correctionsCount, const locale::Locale& locale)
{
    const auto it = locale::findBest(localizedShareDescriptionText, locale);
    ASSERT(it != localizedShareDescriptionText.end());
    return it->second(correctionsCount);
}

} // namespace

VecParams::VecParams(
    size_t commitsCount,
    chrono::TimePoint sinceTimePoint,
    chrono::TimePoint tillTimePoint
)
    : commitsCount_(commitsCount)
    , sinceTimePoint_(sinceTimePoint)
    , tillTimePoint_(tillTimePoint)
{}

sender::EmailTemplateParams
VecParams::toEmailParams(const locale::Locale& locale) const
{
    sender::EmailTemplateParams result;

    auto sinceDate = SimpleDate::fromTimePoint(sinceTimePoint_, locale);
    addDateParam(result, template_param_names::SINCE_DATE_PREFIX, sinceDate);

    auto tillDate = SimpleDate::fromTimePoint(tillTimePoint_, locale);
    addDateParam(result, template_param_names::TILL_DATE_PREFIX, tillDate);

    result.addParam(template_param_names::CORRECTIONS_COUNT, commitsCount_);

    result.addParam(
        template_param_names::SHARE_DESCRIPTION_TEXT,
        http::urlEncode(makeShareDescriptionText(commitsCount_, locale)));

    result.addParam(
        template_param_names::SHARE_TITLE_TEXT,
        http::urlEncode(shareTitle(locale)));

    return result;
}


namespace {

sender::EmailTemplateParams
renderModeIndependentEmailParams(
    const ITypeSpecificParams& typeSpecificParams,
    const sender::ConstantParams& senderConstants,
    const TaskParams& taskParams,
    const locale::Locale& locale)
{
    auto params = typeSpecificParams.toEmailParams(locale);

    params
        .addUrlParam(template_param_names::BLOG_URL, taskParams.blogUrl())
        .addUrlParam(template_param_names::CLUB_URL,
            getBestLocalizedString(senderConstants.clubUrl, locale))
        .addUrlParam(template_param_names::MAPS_URL,
            getBestLocalizedString(senderConstants.mapsUrl, locale))
        .addUrlParam(template_param_names::NMAPS_URL,
            getBestLocalizedString(senderConstants.nmapsUrl, locale))
        .addUrlParam(template_param_names::FEEDBACK_URL,
            getBestLocalizedString(senderConstants.feedbackUrl, locale));

    return params;

}

} // namespace


sender::EmailTemplateParams
renderEmailParams(
    const ITypeSpecificParams& typeSpecificParams,
    const UserInfo& userInfo,
    const sender::ConstantParams& senderConstants,
    const TaskParams& taskParams)
{
    auto params = renderModeIndependentEmailParams(typeSpecificParams, senderConstants, taskParams, userInfo.locale);

    auto unsubscribeUrl = getBestLocalizedString(senderConstants.unsubscribeUrlTemplate, userInfo.locale);

    params.addUrlParam(
        template_param_names::UNSUBSCRIBE_URL,
        unsubscribeUrl);

    if (userInfo.username) {
        params.addParam(template_param_names::USERNAME, *userInfo.username);
    }

    return params;
}

sender::EmailTemplateParams
renderEmailParamsForTestUser(
    const ITypeSpecificParams& typeSpecificParams,
    const sender::ConstantParams& senderConstants,
    const TaskParams& taskParams)
{
    return renderModeIndependentEmailParams(typeSpecificParams, senderConstants, taskParams, DEFAULT_LOCALE);
}

} // maps::wiki::releases_notification
