# Copyright (c) 2018 Trevor L. Davis <trevor.l.davis@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#' Compute net worth
#'
#' Computes net worth for a vector of dates.
#' Computes net worth at the beginning of the day before any transactions have occurred.
#'
#' @param file Filename for a ledger, hledger, or beancount file.
#' @param date Vector of dates to compute net worth for.
#'      For each only the transactions (and price statements) before that date are used in the net worth calculation.
#' @param include Character vector of regular expressions of accounts to include in the net worth calculation.
#'      Use `".*"` to include everything.
#' @param exclude Character vector of regular expressions of accounts to exclude from the net worth calculation.
#'      Use `NULL` to exclude nothing.
#' @param flags Extra flags to pass to `register`.
#'    If using `ledger` may want to try something like `"-X USD"`.
#' @param toolchain Toolchain used to read in register.
#'     Either `"ledger"`, `"hledger"`, `"beancount"`, `"bean-query"`, or `"rledger"`.
#' @param ignore_case logical value of whether to ignore case in regular expressions or not.
#' @return `net_worth` returns a tibble
#' @examples
#'    \dontrun{
#'      example_beancount_file <- system.file("extdata", "example.beancount", package = "ledger")
#'      net_worth(example_beancount_file)
#'      net_worth(example_beancount_file, c("2016-01-01", "2017-01-01", "2018-01-01"))
#'    }
#' @export
net_worth <- function(
	file,
	date = Sys.Date() + 1,
	include = c("^asset", "^liabilit", "^<revalued>"),
	exclude = NULL,
	flags = "-V",
	toolchain = default_toolchain(file),
	ignore_case = TRUE
) {
	df <- bind_rows(lapply(
		date,
		.net_worth_helper,
		file,
		include,
		exclude,
		flags,
		toolchain,
		ignore_case
	))
	for (name in names(df)) {
		if (!name %in% c("commodity", "date")) {
			df[[name]] <- ifelse(is.na(df[[name]]), 0, df[[name]])
		}
	}
	df
}

#' @importFrom dplyr filter
#' @importFrom dplyr group_by
#' @importFrom dplyr left_join
#' @importFrom dplyr summarize
#' @importFrom tidyr spread
#' @importFrom tidyselect one_of
.net_worth_helper <- function(date, file, include, exclude, flags, toolchain, ignore_case) {
	df <- switch(
		toolchain,
		ledger = register_ledger(file, flags, date),
		hledger = register_hledger(
			file,
			flags = flags,
			date = date,
			add_mark = FALSE,
			add_cost = FALSE,
			add_value = FALSE
		),
		beancount = mutate(
			register_beancount(file, date),
			amount = .data$market_value,
			commodity = .data$mv_commodity
		),
		`bean-query` = mutate(
			register_bean_query(file, date),
			amount = .data$market_value,
			commodity = .data$mv_commodity
		),
		rledger = mutate(
			register_rledger(file, date),
			amount = .data$market_value,
			commodity = .data$mv_commodity
		),
		register(file, flags = flags, date = date, toolchain = toolchain)
	)
	include <- paste(include, collapse = "|")
	df <- filter(df, grepl(include, .data$account, ignore.case = ignore_case))
	if (!is.null(exclude)) {
		exclude <- paste(exclude, collapse = "|")
		df <- filter(df, !grepl(exclude, .data$account, ignore.case = ignore_case))
	}
	df <- filter(df, grepl(include, .data$account, ignore.case = ignore_case))
	df <- mutate(df, account = tolower(gsub("^([[:alnum:]]*)?:.*", "\\1", .data$account)))
	df <- mutate(df, account = gsub("<revalued>", "revalued", .data$account))
	df_by <- summarize(group_by(df, .data$account, .data$commodity), total = sum(.data$amount))
	df_by <- tidyr::spread(df_by, .data$account, .data$total)
	old_names <- names(df_by)
	df_nw <- summarize(group_by(df, .data$commodity), net_worth = sum(.data$amount))
	df_nw <- left_join(df_by, df_nw, by = "commodity")
	df_nw <- mutate(df_nw, date = as.Date(date))
	df_nw <- select(df_nw, "date", "commodity", "net_worth", tidyselect::one_of(old_names))
	df_nw
}
