namespace MyOffice.Services.Account;

using MyOffice.Core;
using MyOffice.Data.Models.Accounts;
using MyOffice.Services.Account.Domain;

public partial class AccountService
{
	public async Task<List<AccountCategoryDto>> GetAllCategoriesAsync(Guid userId, CancellationToken cancellationToken = default)
	{
		var categories = await _accountCategoryRepository.GetAllAsync(userId, cancellationToken);

		return _mapper.Map<List<AccountCategoryDto>>(categories);
	}

	public async Task<Exec<AccountCategoryDto, GeneralExecStatus>> GetCategoryAsync(
		Guid userId,
		Guid id,
		CancellationToken cancellationToken = default)
	{
		var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);

		var category = await _accountCategoryRepository.GetAsync(userId, id, cancellationToken);

		if (category == null)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		return result.Set(_mapper.Map<AccountCategoryDto>(category));
	}

	public async Task<Exec<AccountCategoryDto, GeneralExecStatus>> CategoryAddAsync(
		Guid userId,
		AccountCategoryDto input,
		CancellationToken cancellationToken = default)
	{
		if (input == null)
			throw new ArgumentNullException(nameof(input));

		var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);

		var category = new AccountCategory
		{
			Id = Guid.NewGuid(),
			UserId = userId,
			Name = input.Name,
		};

		if (!await _accountCategoryRepository.AddAsync(category, cancellationToken))
		{
			return result.Set(GeneralExecStatus.failure);
		}

		return result.Set(_mapper.Map<AccountCategoryDto>(category));
	}

	public async Task<Exec<AccountCategoryDto, GeneralExecStatus>> CategoryUpdateAsync(
		Guid userId,
		Guid id,
		AccountCategoryDto input,
		CancellationToken cancellationToken = default)
	{
		if (input == null)
			throw new ArgumentNullException(nameof(input));

		var result = new Exec<AccountCategoryDto, GeneralExecStatus>(GeneralExecStatus.success);

		var exists = await _accountCategoryRepository.GetAsync(userId, id, cancellationToken);
		if (exists == null)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		exists.Name = input.Name;

		if (!await _accountCategoryRepository.UpdateAsync(exists, cancellationToken))
		{
			return result.Set(GeneralExecStatus.failure);
		}

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

	public async Task<Exec<AccountCategoryDto, AccountCategoryRemoveResult>> CategoryRemoveAsync(
		Guid userId,
		Guid id,
		CancellationToken cancellationToken = default)
	{
		var result = new Exec<AccountCategoryDto, AccountCategoryRemoveResult>(AccountCategoryRemoveResult.success);

		var exists = await _accountCategoryRepository.GetAsync(userId, id, cancellationToken);
		if (exists == null)
		{
			return result.Set(AccountCategoryRemoveResult.not_found);
		}

		if (exists.Accounts?.Any() == true)
		{
			return result.Set(AccountCategoryRemoveResult.accounts_exists);
		}
		if (!await _accountCategoryRepository.RemoveAsync(exists, cancellationToken))
		{
			return result.Set(AccountCategoryRemoveResult.failure);
		}

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

	public async Task<Exec<AccountDto, GeneralExecStatus>> AccountCategoryRemoveAsync(
		Guid userId,
		Guid accountId,
		Guid categoryId,
		CancellationToken cancellationToken = default)
	{
		var result = new Exec<AccountDto, GeneralExecStatus>(GeneralExecStatus.success);

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

		if (!CanManage(account, userId))
		{
			return result.Set(GeneralExecStatus.forbidden);
		}

		var links = await _accountAccountCategoryRepository.GetAsync(userId, accountId, categoryId, cancellationToken);
		if (links.Count == 0)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		foreach (var link in links)
		{
			await _accountAccountCategoryRepository.RemoveAsync(link, cancellationToken);
		}

		// Account stays tracked after Remove; filter in-memory so the response is not stale.
		account.Categories = account.Categories?
			.Where(c => c.CategoryId != categoryId)
			.ToList();

		return result.Set(account.ToDto<AccountDto>(_mapper));
	}
}
