namespace MyOffice.Services.Account;

using System.Collections.Generic;

using Core;
using Core.Extensions;
using Data.Models.Accounts;
using Domain;
using Item.Domain;

public partial class AccountService
{
	public Exec<List<MotionDto>, MotionAddStatus> MotionAdd(
		Guid userId,
		Guid accountId,
		MotionAddUpdate motion
	)
	{
		if (motion == null)
			throw new ArgumentNullException(nameof(motion));
		if (motion.Item == null)
			throw new ArgumentNullException(nameof(motion.Item));

		var result = new Exec<List<MotionDto>, MotionAddStatus>(MotionAddStatus.success);

		var account = _accountRepository.Get(userId, accountId);
		if (account == null)
		{
			return result.Set(MotionAddStatus.account_not_found);
		}

		var itemExec = _itemService.GetOrCreate(userId, motion.Item);
		if (itemExec.Status != ItemGetOrAddResult.success)
		{
			return result.Set(MotionAddStatus.failure);
		}

		var motionDb = new Motion
		{
			Id = Guid.NewGuid(),
			CreatedOn = DateTime.UtcNow,
			DateTime = motion.Date,
			AccountId = account.Id,
			ItemId = itemExec.Result!.Id,
			Description = motion.Description,
			AmountPlus = motion.Plus,
			AmountMinus = motion.Minus,
		};

		if (!_motionRepository.Add(motionDb))
		{
			return result.Set(MotionAddStatus.failure);
		}

		result.Set(new List<MotionDto>());
		result.Result!.Add(_mapper.Map<MotionDto>(motionDb));

		if (motion.AccountId.IsPresent() && motion.AmountBalancing != 0)
		{
			var accountBalancing = _accountRepository.Get(userId, motion.AccountId!.AsGuid());
			if (accountBalancing != null)
			{
				var balancingName = $"+{account.Name}";
				var itemBalancingExec = _itemService.GetOrCreate(userId, balancingName);
				var plus = motion.AmountBalancing;
				var minus = 0m;
				if (motion.Plus != 0)
				{
					plus = 0;
					minus = motion.AmountBalancing;
				}
				var motionBalancing = new Motion
				{
					Id = Guid.NewGuid(),
					CreatedOn = DateTime.UtcNow,
					DateTime = motion.Date,
					AccountId = accountBalancing.Id,
					ItemId = itemBalancingExec.Result!.Id,
					Description = motion.Description,
					AmountPlus = plus,
					AmountMinus = minus,
				};

				if (_motionRepository.Add(motionBalancing))
				{
					result.Result!.Add(_mapper.Map<MotionDto>(motionBalancing));
				}
			}
		}

		motionDb.Item = itemExec.Result!;

		return result;
	}

	public Exec<MotionDto, MotionUpdateStatus> MotionUpdate(
		Guid userId,
		Guid accountId,
		Guid motionId,
		MotionAddUpdate motion
	)
	{
		if (motion == null)
			throw new ArgumentNullException(nameof(motion));
		if (motion.Item == null)
			throw new ArgumentNullException(nameof(motion.Item));

		var result = new Exec<MotionDto, MotionUpdateStatus>(MotionUpdateStatus.success);

		var exists = _motionRepository.Get(userId, motionId);
		if (exists == null || exists.AccountId != accountId)
		{
			return result.Set(MotionUpdateStatus.not_found);
		}

		var itemExec = _itemService.GetOrCreate(userId, motion.Item);
		if (itemExec.Status != ItemGetOrAddResult.success)
		{
			return result.Set(MotionUpdateStatus.failure);
		}

		exists.Item.Id = itemExec.Result!.Id;
		exists.DateTime = motion.Date;
		exists.Description = motion.Description;
		exists.AmountPlus = motion.Plus;
		exists.AmountMinus = motion.Minus;

		if (!_motionRepository.Update(exists))
		{
			return result.Set(MotionUpdateStatus.failure);

		}

		return result.Set(_mapper.Map<MotionDto>(exists));
	}

	public Exec<List<MotionDto>, GeneralExecStatus> GetMotions(
		Guid userId,
		Guid accountId,
		DateTime dateFrom,
		DateTime dateTo
	)
	{
		var result = new Exec<List<MotionDto>, GeneralExecStatus>(GeneralExecStatus.success);

		var account = _accountRepository.Get(userId, accountId);
		if (account == null)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		var motions = _motionRepository.GetByAccount(accountId, dateFrom.ToUtc(), dateTo.ToUtc());

		return result.Set(_mapper.Map<List<MotionDto>>(motions));
	}

	public Exec<MotionDto, MotionDeleteStatus> MotionRemove(
		Guid userId,
		Guid accountId,
		Guid motionId
	)
	{
		var result = new Exec<MotionDto, MotionDeleteStatus>(MotionDeleteStatus.success);

		var exists = _motionRepository.Get(userId, motionId);
		if (exists == null || exists.AccountId != accountId)
		{
			return result.Set(MotionDeleteStatus.not_found);
		}

		if (!_motionRepository.Remove(exists))
		{
			return result.Set(MotionDeleteStatus.failure);
		}

		return result.Set(_mapper.Map<MotionDto>(exists));
	}
}
