#pragma once

#include <yandex/maps/wiki/common/date_time.h>
#include <maps/libs/introspection/include/comparison.h>

#include <iostream>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

namespace maps::wiki::common {

using IntRange = std::pair<int, int>;
using IntRanges = std::vector<IntRange>;

class Schedule
{
public:
    Schedule(
        const std::string& startDate, const std::string& endDate,
        WeekdayFlags weekdays,
        const std::string& startTime, const std::string& endTime,
        size_t frequency);

    Schedule(
        const std::string& startDate, const std::string& endDate,
        WeekdayFlags weekdays,
        const std::vector<std::string>& departureTimes);

    Schedule(
        const Date& startDate, const Date& endDate,
        WeekdayFlags weekdays,
        const Time& startTime, const Time& endTime,
        size_t frequency);

    Schedule(
        const Date& startDate, const Date& endDate,
        WeekdayFlags weekdays,
        std::vector<Time> departureTimes);

    const Date& startDate() const
    { return startDate_; }

    const Date& endDate() const
    { return endDate_; }

    WeekdayFlags weekdays() const
    { return weekdays_; }

    const std::optional<Time>& startTime() const
    { return startTime_; }

    const std::optional<Time>& endTime() const
    { return endTime_; }

    size_t frequency() const
    { return frequency_; }

    const std::vector<Time>& departureTimes() const
    { return departureTimes_; }

    void setDateInterval(const Date& startDate, const Date& endDate)
    { startDate_ = startDate; endDate_ = endDate; }

    void setWeekdays(WeekdayFlags weekdays)
    { weekdays_ = weekdays; }

    void setTimeInterval(const Time& startTime, const Time& endTime)
    { startTime_ = startTime; endTime_ = endTime; }

    void setFrequency(size_t frequency)
    { frequency_ = frequency; }

    void setDepartureTimes(const std::vector<Time>& departureTimes)
    { departureTimes_ = departureTimes; }

    IntRanges dayRanges() const;
    IntRanges minuteRanges() const;

    bool intersects(const Schedule& other) const;

    auto introspect() const
    {
        return std::tie(
            startDate_, endDate_, weekdays_,
            startTime_, endTime_, frequency_,
            departureTimes_);
    }

private:
    Date startDate_;
    Date endDate_;
    WeekdayFlags weekdays_;

    std::optional<Time> startTime_;
    std::optional<Time> endTime_;
    size_t frequency_;

    std::vector<Time> departureTimes_;
};

using Schedules = std::vector<Schedule>;

std::ostream& operator<<(std::ostream& os, const Schedule& schedule);

using introspection::operator==;

// Schedules in each vector should not intersect
// with each other in (startDate, endDate, weekdays) space

Schedules
unionSchedules(
    const Schedules& lhs,
    const Schedules& rhs);

Schedules
replaceSchedules(
    const Schedules& lhs,
    const Schedules& rhs);

} // namespace maps::wiki::common
