#' Extract data around a time point given window size
#'
#' @param df Dataframe containing a column `date_time`
#' @param date_times The series of date-times that you want a window around
#' @param pre minutes before `date_time` for your start point
#' @param post minutes after `date_time` for your end point
#' @param open_window minutes after open for your end point if the date_time
#'   falls
#'
#' @return Dataframe containing the following columns: * `date_time`: column
#'   containing argument `date_times` * `name`: column indicating if the row
#'   contains `pre` or `post` data * `value`: the actual date-time of the
#'   datapoint extracted * All other columns in `df`- these are the relevant
#'   data points being extracted
#' @export
#'
#' @description This function takes a dataframe (`df`) with a `date_time` column
#'   and uses the `date_times` vector argument along with the window size
#'   provided (`pre` and `post`) to extract a window of data in the other columns of `df`.
#'   To find the start point of the window the following rules are applied:
#'      * If for a specific element in `date_times` the timepoint is just after the the open time (first datapoint available in a day), then the open time is taken as the starting point of the window
#'      * Otherwise, then the start point is simply `date_time - pre`
#'   To find the end point of the window the following rules are applied:
#'      * If specific `date_time` in `date_times` is before the close time available (last datapoint in the day) AND `date_time + post` is after the close time, then the close time is taken as the end point
#'      * If the above is not met and  `date_time` is before the opening time of the same day, then the opening time + `open_window` is used as the end point
#'      * If the above two conditions are not met and the first available timepoint after the `date_time` is the next days opening time, then the next days opening time + `open_window` is used as the end point
#'      * If none of the rules above apply, then the end point is calculated simpley as `date_time + post`
data_window_extract <- function(df, date_times, pre = 30, post = 90, open_window = 60){
  
  # extract possible dates
  possible_date_times <- unique(df$date_time)
  
  # Modify date_times to be between possible date range
  date_times <- date_times[date_times >= min(possible_date_times) & date_times <= max(possible_date_times)]
  
  # Create dataframe of useful dates
  dates <- map_dfr(date_times, ~ tibble(date_time = .,
                                        first_after= possible_date_times[possible_date_times - . > 0] %>%  min ,
                                        same_day_open =  possible_date_times[possible_date_times - (ymd_hm(paste0(lubridate::date(.) , " 00:00" ))) > 0] %>% min,
                                        same_day_close = possible_date_times[possible_date_times - (ymd_hm(paste0(lubridate::date(.) + days(1), " 00:00" ))) < 0] %>% max ,
                                        next_day_open = possible_date_times[possible_date_times - (ymd_hm(paste0(lubridate::date(.) + days(1), " 00:00" ) ) ) >= 0] %>% min))
  
  # find pre and post times
  dates <- dates %>%
    mutate(pre = case_when( date_time > same_day_open &&  date_time - minutes(pre) < same_day_open ~ same_day_open, # just after open
                            TRUE ~ date_time - minutes(pre)),
           post = case_when( date_time < same_day_close && date_time + minutes(post) > same_day_close ~ same_day_close, # just before close.
                             date_time  < same_day_open ~ same_day_open + minutes(open_window), # before open on same day
                             first_after == next_day_open  ~ next_day_open + minutes(open_window), # after close of previous day
                             TRUE ~ date_time + minutes(post) )) %>%
    mutate(pre = map(pre, ~ tibble(pre = possible_date_times[possible_date_times - . <= 0] %>%  max)),
           post = map(post,  ~ tibble(post = possible_date_times[possible_date_times - . >= 0] %>%  min))) %>%
    unnest(c(pre, post)) %>%
    select(date_time, pre, post) %>%
    pivot_longer(c(pre, post))
  
  
  # join on OIS data
  output <- left_join(dates, df, by = c("value" = "date_time"))
  
  return(output)
}




extract_target_yield <- function(df, target = 2, date = "date_time"){
  
  # check date var exists
  
  
  # perform calculation
  df %>% nest_by(!!sym(date), name ) %>%
    rowwise() %>% 
    mutate(above = list(data %>%
                          filter(resid_maturity >= target) %>% 
                          filter(resid_maturity == min(resid_maturity)) %>% 
                          transmute(yield_above = yield, resid_maturity_above = resid_maturity) )  , 
           below = list(data %>% 
                          filter(resid_maturity <= target) %>% 
                          filter(resid_maturity == max(resid_maturity))  %>% 
                          transmute(yield_below = yield, resid_maturity_below = resid_maturity))) %>% 
    unnest(c(above, below) ) %>% 
    transmute(date_time,
              name, 
              yield = yield_above * (target - resid_maturity_below)/(resid_maturity_above - resid_maturity_below) +
                yield_below * (resid_maturity_above - target)/(resid_maturity_above - resid_maturity_below) )
  
  
}

