namespace MyOffice.Services.Item;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using AutoMapper;
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;
	private readonly IMapper _mapper;

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

	public List<ItemCategoryDto> 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 _mapper.Map<List<ItemCategoryDto>>(result);
	}

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

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

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

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

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

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

		var result = new Exec<ItemCategoryDto, 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;

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

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

	public Exec<ItemCategoryDto, ItemCategoryRemoveResult> CategoryRemove(Guid userId, Guid id)
	{
		var result = new Exec<ItemCategoryDto, 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(_mapper.Map<ItemCategoryDto>(exists));
	}

	public List<ItemDto> GetAll(Guid userId)
	{
		return _mapper.Map<List<ItemDto>>(_itemRepository.GetAll(userId));
	}

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

	public List<ItemDto> GetByUncategorized(Guid userId)
	{
		var itemGlobals = _itemGlobalRepository.GetAvailableToUser(userId);
		foreach (var itemGlobal in itemGlobals)
		{
			GetOrCreate(userId, itemGlobal);
		}

		return GetByCategory(userId, userId);
	}

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

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

		item.CategoryId = categoryId;
		_itemRepository.Update(item);

		return result.Set(_mapper.Map<ItemDto>(item));
	}

	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<ItemDto> FindItems(Guid userId, string term, int limit = 15)
	{
		if (term == null)
			throw new ArgumentNullException(nameof(term));

		return _mapper.Map<List<ItemDto>>(_itemRepository.Find(userId, term, limit));
	}

	public GeneralExecStatus UpdateItemsCategory(Guid userId, Guid categoryId, List<Guid> items)
	{
		var category = _itemCategoryRepository.Get(userId, categoryId);
		if (category == null)
		{
			return GeneralExecStatus.not_found;
		}

		foreach (var item in items)
		{
			var dbItem = _itemRepository.GetByGlobal(userId, item);
			if (dbItem != null)
			{
				dbItem.CategoryId = category.Id;
				_itemRepository.Update(dbItem);
			}
		}

		return GeneralExecStatus.success;
	}
}
