package me.gahoi.personalfinance.accounts;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;

import me.gahoi.personalfinance.core.ETF;
import me.gahoi.personalfinance.core.Transaction;
import me.gahoi.personalfinance.pricing.PriceFetcher;
import me.gahoi.personalfinance.tool.BigDecimalHelper;

public interface Account {

	VanguardAccount IRA1 = new VanguardAccount("50240682", "IRA1");

	VanguardAccount IRA2 = new VanguardAccount("19195555", "IRA2");

	VanguardAccount ROTH2 = new VanguardAccount("66415552", "ROTH1");

	VanguardAccount ROTH1 = new VanguardAccount("33966599", "ROTH2");

	VanguardAccount BROKERAGE1 = new VanguardAccount("36666557", "Vanguard Taxable");

	AmeritradeAccount BROKERAGE2 = new AmeritradeAccount();
	
	List<Account> ALL_ACCOUNTS = Arrays.asList(IRA1, IRA2, ROTH1, ROTH2, BROKERAGE1, BROKERAGE2);

	String getHolding(ETF etf);
	
	static Map<ETF, BigDecimal> completePosition() {
		Map<ETF, BigDecimal> total = new HashMap<ETF, BigDecimal>();
		for (ETF etf : ETF.values()) {
			for (Account account : ALL_ACCOUNTS) {
				total.put(etf, BigDecimalHelper.add(total.getOrDefault(etf, BigDecimal.ZERO), account.getHolding(etf)));
			}
		}
		return total;
	}
	
	Map<ETF, String> getPositions();
	
	String getName();
	
	BigDecimal getAvailableCash();
	
	static List<Account> getTaxableAccounts() {
		return Arrays.asList(BROKERAGE1, BROKERAGE2);
	}
	
	static List<Account> getNonTaxableAccount() {
		return Arrays.asList(ROTH1, ROTH2, IRA1, IRA2);
	}
	
	default BigDecimal getWorth() {
		BigDecimal total = getAvailableCash();
		for (Entry<ETF, String> entry : getPositions().entrySet()) {
			total = BigDecimalHelper.add(total,
					BigDecimalHelper.multiply(entry.getValue(), PriceFetcher.INSTANCE.getPrice(entry.getKey())));
		}
		return total;
	}
	
	static BigDecimal getNetWorth() {
		BigDecimal total = BigDecimal.ZERO;
		for (Account account : ALL_ACCOUNTS) {
			total = BigDecimalHelper.add(total, account.getWorth());
		}
		return total;
	}
	
	static void exportToCsv(List<Transaction> txn) {
		NumberFormat $ = NumberFormat.getCurrencyInstance();
		DecimalFormat df = new DecimalFormat("####.000");
		for (ETF etf : ETF.values()) {
			BigDecimal price = PriceFetcher.INSTANCE.getPrice(etf);
			double newPosition = 0;
			double oldPosition = 0;
			for (Account account : ALL_ACCOUNTS) {
				double buySell = account.getBuySell(etf, txn);
				Double position = Double.valueOf(account.getHolding(etf));
				oldPosition+=position;
				newPosition+=(position + buySell);
			}
			System.out.println(String.format("%s,%s,%s,%s", etf.name(),etf.getPrice(),oldPosition, df.format(newPosition)));
		}
		
	}

	static void print(List<Transaction> txn) {
		NumberFormat $ = NumberFormat.getCurrencyInstance();
		DecimalFormat df = new DecimalFormat("#,###.000");
		for (Account account : ALL_ACCOUNTS) {
			System.out.println(String.format("%52s", account.getName()));
			System.out.println(StringUtils.repeat("-", 75));
			System.out.println(String.format("%15s%15s%15s%15s%15s", "Ticker", "Price", "Old Qty", "New Qty", "B/S"));
			System.out.println(String.format("%15s%15s%15s%15s%15s", "------", "-----", "-------", "-------", "---"));
			Set<ETF> all = new HashSet<>();
			all.addAll(account.getPositions().keySet());
			all.addAll(txn.stream().filter(new Predicate<Transaction>() {
				@Override
				public boolean test(Transaction txn) {
					return (account == txn.getAccount());
				}
			}).map(t->t.getEtf()).collect(Collectors.toList()));
			for (ETF etf : all) {
				BigDecimal price = PriceFetcher.INSTANCE.getPrice(etf);
				double buySell = account.getBuySell(etf, txn);
				String position = account.getHolding(etf);
				System.out.println(String.format("%15s%15s%15s%15s%15s", etf.name(), $.format(price), position, 
						df.format(Double.valueOf(position) + buySell), df.format(buySell)));
			}
			System.out.println();
		}
	}
	
	default double getBuySell(final ETF etf, List<Transaction> txn) {
		final Account account = this;
		return txn.stream().filter(new Predicate<Transaction>() {

			@Override
			public boolean test(Transaction txn) {
				return (txn.getEtf() == etf && account == txn.getAccount());
			}
		}).mapToDouble(t->t.getBuyOrSellShares().doubleValue()).sum();
	}
}
