#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <memory>
#include <set>
#include <vector>

struct Time {};
template <typename T, typename U>
struct DifficultStrategy {};
struct PrintVisitor {};

class Ship {
public:
    Ship& setTime(Time* time) {
        _time = time;
        return *this;
    }
    Ship& setStrategy(std::unique_ptr<DifficultStrategy<Ship, bool>> strategy) {
        _strategy = std::move(strategy);
        return *this;
    }
    Ship& setName(const std::string& name) {
        _name = name;
        return *this;
    }
    Ship& setCapacity(int capacity) {
        _capacity = capacity;
        return *this;
    }
    Ship& setMaxCrew(int maxCrew) {
        _maxCrew = maxCrew;
        return *this;
    }
    Ship& setCrew(int crew) {
        _crew = crew;
        return *this;
    }
    Ship& setVisitor(std::unique_ptr<PrintVisitor> visitor) {
        visitor = std::move(_visitor);
        return *this;
    }
    Ship& setArmor(int armor) {
        _armor = armor;
        return *this;
    }
    Ship& setMaxArmor(int maxArmor) {
        _maxArmor = maxArmor;
        return *this;
    }
    Ship& setCannons(int cannons) {
        _canons = cannons;
        return *this;
    }
    Ship& setMaxCannons(int maxCannons) {
        _maxCannons = maxCannons;
        return *this;
    }
    Ship& setDurability(int durability) {
        _durability = durability;
        return *this;
    }
    Ship& setMaxDurability(int maxDurability) {
        _maxDurability = maxDurability;
        return *this;
    }

    const Time* time() const { return _time; }
    const std::string& name() const { return _name; }
    int capacity() const { return _capacity; }
    int maxCrew() const { return _maxCrew; }
    int crew() const { return _crew; }
    int armor() const { return _armor; }
    int maxArmor() const { return _maxArmor; }
    int canons() const { return _canons; }
    int maxCannons() const { return _maxCannons; }
    int durability() const { return _durability; }
    int maxDurability() const { return _maxDurability; }

private:
    // Allow to be created only by std::unique_ptr
    friend std::unique_ptr<Ship> std::make_unique<Ship>();
    Ship() = default;

    Time* _time{};
    std::unique_ptr<DifficultStrategy<Ship, bool>> _strategy{};
    std::string _name{};
    int _capacity{-1};
    int _maxCrew{-1};
    int _crew{10};
    std::unique_ptr<PrintVisitor> _visitor{};
    int _armor{0};
    int _maxArmor{-1};
    int _canons{0};
    int _maxCannons{-1};
    int _durability{100};
    int _maxDurability{-1};
};

class ShipBuilder {
public:
    ShipBuilder()
        : _ship(std::make_unique<Ship>()) {}

    [[nodiscard]] std::unique_ptr<Ship> build() {
        if (!_ship) {
            std::cout << "Builder may be use once\n";
            return nullptr;
        }

        // Check mandatory fields
        if (!_ship->time() ||
            _ship->name().empty() ||
            _ship->capacity() == -1 ||
            _ship->maxCrew() == -1 ||
            _ship->maxArmor() == -1 ||
            _ship->maxCannons() == -1 ||
            _ship->maxDurability() == -1) {
            std::cout << "Mandatory fields are not set!\n";
            return nullptr;
        }

        return std::move(_ship);
    }

    ShipBuilder& setTime(Time* time) {
        _ship->setTime(time);
        return *this;
    }
    ShipBuilder& setStrategy(std::unique_ptr<DifficultStrategy<Ship, bool>> strategy) {
        _ship->setStrategy(std::move(strategy));
        return *this;
    }
    ShipBuilder& setName(const std::string& name) {
        _ship->setName(name);
        return *this;
    }
    ShipBuilder& setCapacity(int capacity) {
        _ship->setCapacity(capacity);
        return *this;
    }
    ShipBuilder& setMaxCrew(int maxCrew) {
        _ship->setMaxCrew(maxCrew);
        return *this;
    }
    ShipBuilder& setCrew(int crew) {
        _ship->setCrew(crew);
        return *this;
    }
    ShipBuilder& setVisitor(std::unique_ptr<PrintVisitor> visitor) {
        _ship->setVisitor(std::move(visitor));
        return *this;
    }
    ShipBuilder& setArmor(int armor) {
        _ship->setArmor(armor);
        return *this;
    }
    ShipBuilder& setMaxArmor(int maxArmor) {
        _ship->setMaxArmor(maxArmor);
        return *this;
    }
    ShipBuilder& setCannons(int cannons) {
        _ship->setCannons(cannons);
        return *this;
    }
    ShipBuilder& setMaxCannons(int maxCannons) {
        _ship->setMaxCannons(maxCannons);
        return *this;
    }
    ShipBuilder& setDurability(int durability) {
        _ship->setDurability(durability);
        return *this;
    }
    ShipBuilder& setMaxDurability(int maxDurability) {
        _ship->setMaxDurability(maxDurability);
        return *this;
    }

private:
    std::unique_ptr<Ship> _ship;
};

int main() {
    auto ship = ShipBuilder().build();
    if (ship) {
        std::cout << "Ship name: " << ship->name() << '\n';
    }

    Time time;
    auto ship2 = ShipBuilder()
                     .setTime(&time)
                     .setName("Black Pearl")
                     .setCapacity(500)
                     .setMaxCrew(200)
                     .setMaxArmor(1000)
                     .setMaxCannons(20)
                     .setMaxDurability(1000)
                     .build();

    if (ship2) {
        std::cout << "Ship name: " << ship2->name() << '\n';
    }
}
