class AdminService {

	public SymbolInfo add(String code, String provider) throws IOException {
		Stopwatch sw = Stopwatch.createStarted();

		Pair pair = pairService.getForCodeAndProvider(code, provider);

		if (pair != null) {
			throw PairException.alreadyExists(pair.getSymbol(), provider);
		}

		LookupDetails details = lookupService.getDetails(code, provider);

		String source = details.getSource();
		String currency = details.getCurrency();
		String name = details.getName();
		String exchange = details.getExchange();

		// check if currency exists
		if (!symbolService.getForCodeWithNoCurrency(currency).isPresent()) {
			throw PairException.currencyNotFound(currency);
		}

		Optional<Symbol> sourceSymbol = symbolService.getForCode(source);

		if(sourceSymbol.isPresent()) {
			if(sourceSymbol.get().getCurrency() == null) {
				throw AdminException.currencyAlreadyExists(source);
			}
		} else {
			symbolService.addSymbol(source, name, null, currency);
		}

		pair = pairService.create(code, name, source, currency, exchange, provider);

		final LatestRateProvider rProvider = rateProviderService.getLatestProvider(ProviderType.STOCK);

		Quote latestQuote = rProvider.getLatestRate(code);

		if (latestQuote == null) {
			throw SymbolException.UNAVAILABLE(code);
		}

		// TODO immutability ! copy data
		latestQuote.setCode(source);
		latestQuote.setCurrency(currency);

		List<Quote> historicalQuotes = new ArrayList<>();

		final HistoricalRateProvider hRProvider = rateProviderService.getHistoricalProvider(ProviderType.STOCK);

		try {
			historicalQuotes.addAll(hRProvider.getHistoricalRates(code));
		} catch (IOException | RuntimeException e) {
			// generally, if provider cannot get historical data, it throws exception
			log.warn("Cannot get historical data for: {} (provider: {})", code, provider);
		}

		for (Quote q : historicalQuotes) {
			q.setCode(source);
			q.setCurrency(currency);
		}

		historicalQuotes.removeIf(q -> q.getTime().compareTo(latestQuote.getTime()) == 0);
		
		historicalQuotes.add(latestQuote);

		rateService.addHistoricalRates(historicalQuotes);
		rateService.addLatestRate(latestQuote);

		log.info("{} created in: {}", pair, sw);

		return new SymbolInfo(pair.getSymbol(), pair.getName(), !historicalQuotes.isEmpty(), latestQuote);
	}

}