const VALID_BAR_SIZES = (
    Second(1), Second(5), Second(10), Second(15), Second(30),
    Minute(1), Minute(2), Minute(3), Minute(5), Minute(10), Minute(15), Minute(20), Minute(30),
    Hour(1), Hour(2), Hour(3), Hour(4), Hour(8),
    Day(1),
    Week(1),
    Month(1),
)

const VALID_DURATION_UNITS = (Second, Day, Week, Month, Year)

const VALID_HISTORICAL_DATA_TYPE = (
    "TRADES",
    "MIDPOINT",
    "BID",
    "ASK",
    "BID_ASK",
    "ADJUSTED_LAST",
    "HISTORICAL_VOLATILITY",
    "OPTION_IMPLIED_VOLATILITY",
    "REBATE_RATE",
    "FEE_RATE",
    "YIELD_BID",
    "YIELD_ASK",
    "YIELD_BID_ASK",
    "YIELD_LAST",
)

# TODO Timezone
function historical_data(
    ib::IB,
    asset::Asset;
    end_datetime::DateTime = now(),
    duration::TimePeriod = Week(1),
    barsize::TimePeriod = Minute(1),
    use_regular_trading_hours::Bool=true,
    datatype::String = "MIDPOINT",
) 
    check_end_datetime(end_datetime)
    check_duration(dt)
    check_barsize(sz)
    check_historical_data_type(datatype)
    datas = []
    while true
        data = ib.reqHistoricalData(
            asset,
            endDateTime = end_datetime,
            durationStr = duration,
            barsizeSetting = barsize,
            whatToShow = datatype,
            useRTH = use_regular_trading_hours
        )
        @info data
        data === nothing && break
        push!(datas, data)
        @info data[1].data
        break
    end
end

function duration_str(dt::TimePeriod)
    check_duration(dt)
    unit = dt isa Second ? "S" :
        dt isa Day ? "D" :
        dt isa Week ? "W" :
        dt isa Month ? "M" :
        dt isa Year ? "Y" :
        error("This shouldn't happen")
    return "$(dt.value) $unit"
end

function barsize_str(sz::TimePeriod)
    check_barsize(sz)
    unit = sz isa Second ? "secs" :
        sz isa Minute ? (sz == Minute(1) ? "min" : "mins") :
        sz isa Hour ? (sz == Hour(1) ? "hour" : "hours") :
        sz isa Day ? "day" :
        sz isa Week ? "week" :
        sz isa Month ? "month" :
        error("This shouldn't happen")
    return "$(dt.value) $unit"
end

function check_end_datetime(t::DateTime)
    t > now() && error("End date time cannot be in the future: $t")
    return nothing
end

function check_duration(dt::TimePeriod)
    idx = findfirst(T -> typeof(dt) === T, VALID_DURATION_UNITS)
    idx === nothing && error("Invalid duration, expected unit of one of $(VALID_DURATION_UNITS)")
    return nothing
end

function check_barsize(sz::TimePeriod)
    Tlargest = typeof(first(canonicalize(sz).periods))
    sz = round(sz, Tlargest)
    idx = findfirst(x -> x == sz, VALID_BAR_SIZES)
    idx === nothing && error("Invalid dt $sz, expected one of $VALID_BAR_SIZES")
    return nothing
end

function check_historical_data_type(type::String)
    idx = findfirst(t -> t == type, VALID_HISTORICAL_DATA_TYPE)
    idx === nothing && error("Invalid historical data type, expected unit of one of $(VALID_HISTORICAL_DATA_TYPE)")
    return nothing
end
