#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:
    void setCrew(int crew) { _crew = crew; }
    void setArmor(int armor) { _armor = armor; }
    void setCannons(int cannons) { _canons = cannons; }
    void setDurability(int durability) { _durability = durability; }

    const std::string& name() const { return _name; }
    int capacity() const { return _capacity; }
    int crew() const { return _crew; }
    int armor() const { return _armor; }
    int canons() const { return _canons; }
    int durability() const { return _durability; }

private:
    // Allow to be created only by std::unique_ptr
    friend std::unique_ptr<Ship> std::make_unique<Ship>();
    friend class ShipBuilder;
    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->_time = time;
        return *this;
    }
    ShipBuilder& setStrategy(std::unique_ptr<DifficultStrategy<Ship, bool>> strategy) {
        _ship->_strategy = std::move(strategy);
        return *this;
    }
    ShipBuilder& setName(const std::string& name) {
        _ship->_name = name;
        return *this;
    }
    ShipBuilder& setCapacity(int capacity) {
        _ship->_capacity = capacity;
        return *this;
    }
    ShipBuilder& setMaxCrew(int maxCrew) {
        _ship->_maxCrew = maxCrew;
        return *this;
    }
    ShipBuilder& setCrew(int crew) {
        _ship->_crew = crew;
        return *this;
    }
    ShipBuilder& setVisitor(std::unique_ptr<PrintVisitor> visitor) {
        _ship->_visitor = std::move(visitor);
        return *this;
    }
    ShipBuilder& setArmor(int armor) {
        _ship->_armor = armor;
        return *this;
    }
    ShipBuilder& setMaxArmor(int maxArmor) {
        _ship->_maxArmor = maxArmor;
        return *this;
    }
    ShipBuilder& setCannons(int cannons) {
        _ship->_canons = cannons;
        return *this;
    }
    ShipBuilder& setMaxCannons(int maxCannons) {
        _ship->_maxCannons = maxCannons;
        return *this;
    }
    ShipBuilder& setDurability(int durability) {
        _ship->_durability = durability;
        return *this;
    }
    ShipBuilder& setMaxDurability(int maxDurability) {
        _ship->_maxDurability = 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';
    }
}
