namespace MyOffice.Data.Repositories.Account;

using Microsoft.EntityFrameworkCore;
using Models.Accounts;
using MyOffice.Data.Repositories;

public class AccountRepository : AppRepository<Account>, IAccountRepository
{
	public List<Account> GetAll(Guid userId)
	{
		return _context.Accounts!
			.Include(x => x.CurrencyGlobal)
			.Include(x => x.Categories)!
			.ThenInclude(x => x.Category)
			.Include(x => x.AccessRights)!
			.ThenInclude(x => x.User)
			.Include(x => x.Motions)!
			.Where(x => x.AccessRights!.Any(a => a.UserId == userId))
			.ToList();
	}

	public List<Account> GetByCategory(Guid userId, Guid categoryId)
	{
		return _context.Accounts!
			.Include(x => x.CurrencyGlobal)
			.Include(x => x.Categories)!
			.ThenInclude(x => x.Category)
			.Include(x => x.AccessRights)!
			.ThenInclude(x => x.User)
			.Include(x => x.Motions)!
			.Where(x => x.Categories!.Any(c => c.CategoryId == categoryId) && x.AccessRights!.Any(a => a.UserId == userId))
			.ToList();
	}

	public List<AccountDetailed> GetByCategoryDetailed(Guid userId, Guid categoryId)
	{
		return _context.Accounts!
			.Include(x => x.CurrencyGlobal)
			.Include(x => x.Categories)!
			.ThenInclude(x => x.Category)
			.Include(x => x.AccessRights)!
			.ThenInclude(x => x.User)
			.Where(x => x.Categories!.Any(c => c.CategoryId == categoryId) && x.AccessRights!.Any(a => a.UserId == userId))
			.Select(x => new AccountDetailed
			{
				Account = x,
				TotalPlus = x.Motions!.Sum(m => m.AmountPlus),
				TotalMinus = x.Motions!.Sum(m => m.AmountMinus),
			})
			.ToList();
	}

	public AccountDetailed? GetByIdDetailed(Guid userId, Guid id)
	{
		return _context.Accounts!
			.Include(x => x.CurrencyGlobal)
			.Include(x => x.Categories)!
			.ThenInclude(x => x.Category)
			.Include(x => x.AccessRights)!
			.ThenInclude(x => x.User)
			.Where(x => x.Id == id && x.AccessRights!.Any(a => a.UserId == userId))
			.Select(x => new AccountDetailed
			{
				Account = x,
				TotalPlus = x.Motions!.Sum(m => m.AmountPlus),
				TotalMinus = x.Motions!.Sum(m => m.AmountMinus),
			})
			.FirstOrDefault();
	}

	public bool Add(Account account)
	{
		return AddBase(account) > 0;
	}

	public bool Delete(Account account)
	{
		return RemoveBase(account) > 0;
	}

	public Account? Get(Guid userId, Guid id)
	{
		return _context.Accounts!
			.Include(x => x.Categories)!
			.ThenInclude(x => x.Category)
			.Include(x => x.AccessRights)!
			.ThenInclude(x => x.User)
			.Include(x => x.Motions)!
			.FirstOrDefault(x => x.Id == id && x.AccessRights!.Any(r => r.UserId == userId));
	}

	public bool Update(Account account)
	{
		return UpdateBase(account) > 0;
	}

	public bool Remove(Account account)
	{
		return RemoveBase(account) > 0;
	}

	public List<Account> FindAccounts(Guid userId, string term)
	{
		return _context.Accounts
			.Where(x => x.AccessRights!.Any(a => a.UserId == userId))
			.Where(x => x.Name.ToLower().Contains(term.ToLower()))
			.ToList();
	}

	public decimal GetPeriodIncome(Guid userId, DateTime from, DateTime to)
	{
		return _context.Accounts
			.Where(x => x.AccessRights!.Any(u => u.UserId == userId))
			.Include(x => x.CurrencyGlobal!)
			.ThenInclude(x => x.Currencies!)
			.ThenInclude(x => x.CurrentRate!)
			.Select(x => new {
				Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId),
				Amount = x.Motions!
					.Where(x => !x.Item!.Category!.IsInternal)
					.Where(x => x.DateTime >= from && x.DateTime <= to)
					.Sum(m => m.AmountPlus)
			})
			.ToList()
			.Select(x => x.Amount * (x.Currency?.CurrentRate?.Rate * x.Currency?.CurrentRate?.Quantity))
			.Sum() ?? 0;
	}

	public decimal GetPeriodOutcome(Guid userId, DateTime from, DateTime to)
	{
		return _context.Accounts
			.Where(x => x.AccessRights!.Any(u => u.UserId == userId))
			.Include(x => x.CurrencyGlobal!)
			.ThenInclude(x => x.Currencies!)
			.ThenInclude(x => x.CurrentRate!)
			.Select(x => new {
				Currency = x.CurrencyGlobal!.Currencies!.FirstOrDefault(x => x.UserId == userId),
				Amount = x.Motions!
					.Where(x => !x.Item!.Category!.IsInternal)
					.Where(x => x.DateTime >= from && x.DateTime <= to)
					.Sum(m => m.AmountMinus)
			})
			.ToList()
			.Select(x => x.Amount * (x.Currency?.CurrentRate?.Rate * x.Currency?.CurrentRate?.Quantity))
			.Sum() ?? 0;
	}

	public List<AccountSimple> GetRestAtDate(Guid userId, DateTime date)
	{
		return _context.AccountAccesses!
			.Where(x => x.UserId == userId)
			.Include(x => x.Account!)
			.ThenInclude(x => x.CurrencyGlobal!)
			.ThenInclude(x => x.Currencies!)
			.ThenInclude(x => x.CurrentRate!)
			.Include(x => x.Account)
			.ThenInclude(x => x!.Motions)
			.Select(x => new 
			{
				Id = x.AccountId,
				Name = x.Account!.Name,
				Type = x.Type,
				Currency = x.Account!.CurrencyGlobal!.Currencies!.FirstOrDefault(c => c.UserId == userId),
				CurrentRate = x.Account!.CurrencyGlobal!.Currencies!.FirstOrDefault(c => c.UserId == userId)!.CurrentRate,
				Plus = x.Account.Motions!
					.Where(x => !x.DeletedOn.HasValue)
					.Sum(m => m.AmountPlus),
				Minus = x.Account.Motions!
					.Where(x => !x.DeletedOn.HasValue)
					.Sum(m => m.AmountMinus),
			})
			.ToList()
			.Select(x => new AccountSimple
			{
				Id = x.Id,
				Name = x.Name,
				Type = x.Type,
				CurrencyName = x.Currency!.Name,
				CurrencyShortName = x.Currency!.ShortName,
				CurrencyRate = x.CurrentRate?.Rate,
				CurrencyQuantity = x.CurrentRate?.Quantity,
				TotalMinus = x.Minus,
				TotalPlus = x.Plus,
				Balance = x.Plus - x.Minus,
			})
			.ToList();
	}
}