namespace MyOffice.Web.Controllers;

using AutoMapper;
using Core;
using Core.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Models.Account;
using Models.Item;
using Models.Motion;
using MyOffice.Web.Infrastructure.Attributes;
using Services.Account;
using Services.Account.Domain;
using Services.Item;

[Authorize]
[ApiController]
[Route("api/accounts")]
[DefaultFromBody]
public class AccountController : BaseApiController
{
	private readonly ILogger<AccountController> _logger;
	private readonly IMapper _mapper;
	private readonly AccountService _accountService;
	private readonly ItemService _itemService;

	public AccountController(
		ILogger<AccountController> logger,
		IMapper mapper,
		AccountService accountService,
		ItemService itemService
	)
	{
		_logger = logger;
		_mapper = mapper;
		_accountService = accountService;
		_itemService = itemService;
	}

	[HttpGet]
	public async Task<ObjectResult> AccountsGet([FromQuery] string category)
	{
		var list = await _accountService.GetByCategoryDetailedAsync(UserId, category!.AsGuid());

		return OkResponse(_mapper.Map<List<AccountDetailedViewModel>>(list.OrderBy(x => x.Account.Name).ToList()));
	}

	[HttpGet("{id}")]
	public async Task<ObjectResult> AccountGet(string id)
	{
		var exec = await _accountService.GetByIdDetailedAsync(UserId, id!.AsGuid());

		return MapGeneralExec(
			exec,
			result => OkResponse(_mapper.Map<AccountDetailedViewModel>(result)),
			notFoundDetail: "Account not found.",
			failureDetail: "Failed to load account.");
	}

	[HttpGet("{id}/motions")]
	public async Task<ObjectResult> MotionsGet(string id, [FromQuery] MotionsGetRequest request)
	{
		var exec = await _accountService.GetMotionsAsync(
			UserId,
			id!.AsGuid(),
			request.From.StartOfDay(),
			request.To.EndOfDay());

		return MapGeneralExec(
			exec,
			result => OkResponse(_mapper.Map<List<MotionViewModel>>(result)),
			notFoundDetail: "Account not found.",
			failureDetail: "Failed to load motions.");
	}

	[HttpPost("{id}/motions")]
	public async Task<ObjectResult> MotionsPost(string id, MotionRequest motion)
	{
		var exec = await _accountService.MotionAddAsync(
			UserId,
			id.AsGuid(),
			_mapper.Map<MotionAddUpdate>(motion));

		switch (exec.Status)
		{
			case MotionAddStatus.account_not_found:
				return ProblemNotFoundResponse("Account not found.");
			case MotionAddStatus.forbidden:
				return ProblemForbiddenResponse("Write access required.");
			case MotionAddStatus.failure:
				return ProblemBadResponse("Adding motion failed.");
			case MotionAddStatus.success:
				return Ok(_mapper.Map<MotionViewModel[]>(exec.Result!));

			default:
				throw new NotSupportedException(exec.Status.ToString());
		}
	}

	[HttpPut("{id}/motions/{motionId}")]
	public async Task<ObjectResult> MotionsPut(string id, string motionId, MotionRequest motion)
	{
		var exec = await _accountService.MotionUpdateAsync(
			UserId,
			id.AsGuid(),
			motionId.AsGuid(),
			_mapper.Map<MotionAddUpdate>(motion));

		switch (exec.Status)
		{
			case MotionUpdateStatus.not_found:
				return ProblemNotFoundResponse("Motion not found.");
			case MotionUpdateStatus.forbidden:
				return ProblemForbiddenResponse("Write access required.");
			case MotionUpdateStatus.failure:
				return ProblemBadResponse("Updating motion failed.");
			case MotionUpdateStatus.success:
				return OkResponse(_mapper.Map<List<MotionViewModel>>(exec.Result!));

			default:
				throw new NotSupportedException(exec.Status.ToString());
		}
	}

	[HttpDelete("{id}/motions/{motionId}")]
	public async Task<ObjectResult> MotionsDelete(string id, string motionId)
	{
		var exec = await _accountService.MotionRemoveAsync(
			UserId,
			id.AsGuid(),
			motionId.AsGuid());

		switch (exec.Status)
		{
			case MotionDeleteStatus.not_found:
				return ProblemNotFoundResponse("Motion not found.");
			case MotionDeleteStatus.forbidden:
				return ProblemForbiddenResponse("Write access required.");
			case MotionDeleteStatus.failure:
				return ProblemBadResponse("Deliting motion failed.");
			case MotionDeleteStatus.success:
				return OkResponse(_mapper.Map<MotionViewModel>(exec.Result!));

			default:
				throw new NotSupportedException(exec.Status.ToString());
		}
	}

	[HttpGet("~/api/items")]
	public async Task<ObjectResult> FindItems([FromQuery] string term, CancellationToken cancellationToken)
	{
		var itemsDto = await _itemService.FindItemsAsync(UserId, term, cancellationToken: cancellationToken);

		var items = _mapper.Map<List<ItemViewModel>>(itemsDto);

		if (term.StartsWith("+") && term.Length > 1)
		{
			var foundAccounts = await _accountService.FindAccountsAsync(UserId, term.Substring(1), cancellationToken);
			var accounts = foundAccounts.OrderByDescending(x => x.Name);

			foreach (var account in accounts)
			{
				var accountName = $"+{account.Name}";
				var item = items.FirstOrDefault(x => x.Name.IsPresent() && x.Name!.Length > 1 && x.Name.Substring(1) == accountName);

				if (item == null)
				{
					items.Add(new ItemViewModel
					{
						Name = accountName,
						AccountId = account.Id.ToShort(),
					});
				}
				else
				{
					item.AccountId = account.Id.ToShort();
				}
			}
		}

		return OkResponse(_mapper
			.Map<List<ItemViewModel>>(items)
			.OrderBy(x => x.AccountId)
			.ThenBy(x => x.Name)
		);
	}
}
