namespace MyOffice.Services.Item;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Domain;
using MyOffice.Core;
using MyOffice.Data.Models.Accounts;
using MyOffice.Data.Models.Items;
using MyOffice.Data.Repositories.Item;
using MyOffice.Services.Account.Domain;

public class ItemService
{
	private readonly IItemCategoryRepository _itemCategoryRepository;
	private readonly IItemRepository _itemRepository;
	private readonly IItemGlobalRepository _itemGlobalRepository;

	public ItemService(
		IItemCategoryRepository itemCategoryRepository,
		IItemRepository itemRepository,
		IItemGlobalRepository itemGlobalRepository
	)
	{
		_itemCategoryRepository = itemCategoryRepository;
		_itemRepository = itemRepository;
		_itemGlobalRepository = itemGlobalRepository;

	}

	public List<ItemCategory> GetAllCategories(Guid userId)
	{
		var result = _itemCategoryRepository.GetAll(userId);
		if (result.All(x => x.Id != userId))
		{
			var category = new ItemCategory
			{
				Id = userId,
				UserId = userId,
				Name = "UnCategorized",
			};
			_itemCategoryRepository.Add(category);

			result.Add(_itemCategoryRepository.Get(userId, userId)!);
		}
		return result;
	}

	public Exec<ItemCategory, GeneralExecStatus> CategoryAdd(Guid userId, ItemCategory category)
	{
		if (category == null)
			throw new ArgumentNullException(nameof(category));

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

		category.Id = Guid.NewGuid();
		category.UserId = userId;
		category.IsInternal = category.IsInternal;
		category.Items = new List<Item>();

		if (!_itemCategoryRepository.Add(category))
		{
			return result.Set(GeneralExecStatus.failure);
		}

		return result.Set(category);
	}

	public Exec<ItemCategory, GeneralExecStatus> CategoryUpdate(Guid userId, Guid id, ItemCategory category)
	{
		if (category == null)
			throw new ArgumentNullException(nameof(category));

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

		var exists = _itemCategoryRepository.Get(userId, id);
		if (exists == null)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		exists.Name = category.Name;
		exists.IsInternal = category.IsInternal;
		exists.Items = new List<Item>();

		if (!_itemCategoryRepository.Update(exists))
		{
			return result.Set(GeneralExecStatus.failure);
		}

		return result.Set(exists);
	}

	public Exec<ItemCategory, ItemCategoryRemoveResult> CategoryRemove(Guid userId, Guid id)
	{
		var result = new Exec<ItemCategory, ItemCategoryRemoveResult>(ItemCategoryRemoveResult.success);

		var exists = _itemCategoryRepository.Get(userId, id);
		if (exists == null)
		{
			return result.Set(ItemCategoryRemoveResult.not_found);
		}

		if (exists.Items.Any())
		{
			return result.Set(ItemCategoryRemoveResult.accounts_exists);
		}
		if (!_itemCategoryRepository.Remove(exists))
		{
			return result.Set(ItemCategoryRemoveResult.failure);
		}

		return result.Set(exists);
	}

	public List<Item> GetAll(Guid userId)
	{
		return _itemRepository.GetAll(userId);
	}

	public List<Item> GetByCategory(Guid userId, Guid categoryId)
	{
		return _itemRepository.GetByCategory(userId, categoryId);
	}

	public Exec<Item, GeneralExecStatus> Update(Guid userId, Guid motionId, Guid categoryId)
	{
		var result = new Exec<Item, GeneralExecStatus>(GeneralExecStatus.success);

		var motion = _itemRepository.GetByGlobal(userId, motionId);
		if (motion == null)
		{
			return result.Set(GeneralExecStatus.not_found);
		}

		motion.Category!.Id = categoryId;
		_itemRepository.Update(motion);

		return result.Set(motion);
	}

	public Exec<Item, ItemGetOrAddResult> GetOrCreate(Guid userId, string name)
	{
		if (name == null)
			throw new ArgumentNullException(nameof(name));

		var result = new Exec<Item, ItemGetOrAddResult>(ItemGetOrAddResult.success);

		var itemGlobal = _itemGlobalRepository.GetByName(name);
		if (itemGlobal == null)
		{
			// add global motion
			itemGlobal = new ItemGlobal
			{
				Id = Guid.NewGuid(),
				Name = name.Trim(),
			};

			if (!_itemGlobalRepository.Add(itemGlobal))
			{
				return result.Set(ItemGetOrAddResult.failure);
			}

			return GetOrCreate(userId, itemGlobal);
		}

		return GetOrCreate(userId, itemGlobal);
	}

	private Exec<Item, ItemGetOrAddResult> GetOrCreate(Guid userId, ItemGlobal itemGlobal)
	{
		if (itemGlobal == null)
			throw new ArgumentNullException(nameof(itemGlobal));

		var result = new Exec<Item, ItemGetOrAddResult>(ItemGetOrAddResult.success);

		var item = _itemRepository.GetByGlobal(userId, itemGlobal.Id);
		if (item == null)
		{
			// add item to UnCategorized category
			item = new Item
			{
				CategoryId = userId,
				ItemGlobalId = itemGlobal.Id,
			};
			_itemRepository.Add(item);
		}

		item.ItemGlobal = itemGlobal;
		return result.Set(item);
	}

	public List<Item> FindItems(Guid userId, string term, int limit = 15)
	{
		if (term == null)
			throw new ArgumentNullException(nameof(term));

		return _itemRepository.Find(userId, term, limit);
	}
}
