namespace MyOffice.Services.Currency;

using Core;
using Core.Extensions;
using Data.Models.Currencies;
using Data.Repositories.Currency;
using MyOffice.Services.Currency.Domain;

public class CurrencyService
{
	private readonly ICurrencyGlobalRepository _currencyGlobalRepository;
	private readonly ICurrencyRepository _currencyRepository;
	private readonly ICurrencyRateRepository _currencyRateRepository;

	public CurrencyService(
		ICurrencyGlobalRepository currencyGlobalRepository,
		ICurrencyRepository currencyRepository,
		ICurrencyRateRepository currencyRateRepository
	)
	{
		_currencyGlobalRepository = currencyGlobalRepository;
		_currencyRepository = currencyRepository;
		_currencyRateRepository = currencyRateRepository;
	}

	public List<CurrencyGlobal> GetGlobalAll()
	{
		return _currencyGlobalRepository.GetAll();
	}

	public List<Currency> GetAll(Guid userId)
	{
		return _currencyRepository.GetAll(userId);
	}

	public Dictionary<Currency, CurrencyRate?> GetAllWithRates(Guid userId)
	{
		var list = _currencyRepository.GetAll(userId);
		return list.ToDictionary(
			x => x,
			x => _currencyRateRepository.GetLastRates(x.Id).FirstOrDefault()
		);
	}

	public Exec<Currency, CurrencyAddStatus> CurrencyAdd(Guid userId, Currency currency)
	{
		if (currency == null)
			throw new ArgumentNullException(nameof(currency));

		var result = new Exec<Currency, CurrencyAddStatus>(CurrencyAddStatus.success);

		var exists = _currencyRepository.GetByGlobalCurrency(userId, currency.CurrencyGlobalId);
		if (exists != null)
		{
			return result.Set(exists, CurrencyAddStatus.exists);
		}

		currency.Id = Guid.NewGuid();
		currency.UserId = userId;
		if (!_currencyRepository.Add(currency))
		{
			return result.Set(CurrencyAddStatus.failed);
		}

		return result.Set(currency);
	}

	public Exec<Currency, CurrencyEditStatus> CurrencyUpdate(
		Guid userId, 
		Guid currencyId, 
		CurrencyEdit currency
	)
	{
		if (currency == null)
			throw new ArgumentNullException(nameof(currency));

		var result = new Exec<Currency, CurrencyEditStatus>(CurrencyEditStatus.success);

		var exists = _currencyRepository.Get(userId, currencyId);
		if (exists == null)
		{
			return result.Set(CurrencyEditStatus.not_found);
		}

		exists.Name = currency.Name;
		exists.ShortName = currency.ShortName;
		exists.IsPrimary = currency.IsPrimary;

		if (!_currencyRepository.Update(exists))
		{
			return result.Set(CurrencyEditStatus.failed);
		}

		if (exists.IsPrimary)
		{
			var primaries = _currencyRepository.GetPrimaries(userId);
			foreach (var primary in primaries)
			{
				if (primary.Id != exists.Id)
				{
					primary.IsPrimary = false;
					_currencyRepository.Update(primary);
				}
			}
		}

		return result.Set(exists);
	}

	public Exec<CurrencyRate, CurrencyAddRateStatus> CurrencyRateAdd(Guid userId, Guid currencyId, CurrencyRate currencyRate)
	{
		if (currencyRate == null)
			throw new ArgumentNullException(nameof(currencyRate));

		var result = new Exec<CurrencyRate, CurrencyAddRateStatus>(CurrencyAddRateStatus.success);

		var currency = _currencyRepository.Get(userId, currencyId);
		if (currency == null)
		{
			return result.Set(CurrencyAddRateStatus.not_found);
		}

		currencyRate.CurrencyId = currency.Id;
		currencyRate.DateTime = currencyRate.DateTime.Date;

		var rates = _currencyRateRepository.GetAtDate(currencyRate.CurrencyId, currencyRate.DateTime);
		var rate = rates.Find(x => x.Rate == currencyRate.Rate);
		if (rate == null)
		{
			if (!_currencyRateRepository.AddRate(currencyRate))
			{
				return result.Set(CurrencyAddRateStatus.failed);
			}

			rate = currencyRate;

			if (rate.DateTime.EndOfDay() <= DateTime.UtcNow.EndOfDay())
			{
				currency.CurrentRateId = rate.Id;
				_currencyRepository.Update(currency);
			}
		}

		return result.Set(rate);
	}
}
