using API.Controllers;
using API.Models.Internal.Altersgruppen;
using API.Repository.AgeGroupRepo;
using Microsoft.AspNetCore.Mvc;
using Moq;

namespace API.Tests.Controllers;

public class AgeGroupControllerTests
{
    private readonly Mock<IAgeGroupService> _mockService;
    private readonly AgeGroupController _controller;

    public AgeGroupControllerTests()
    {
        _mockService = new Mock<IAgeGroupService>();
        _controller = new AgeGroupController(_mockService.Object);
    }

    [Fact]
    public async Task GetAll_ReturnsOkWithAllGroups()
    {
        // Arrange
        var groups = new List<AltersGruppe>
        {
            new AltersGruppe { Id = "1", Name = "U10", StartingAge = 8, EndingAge = 10 },
            new AltersGruppe { Id = "2", Name = "U12", StartingAge = 10, EndingAge = 12 }
        };
        _mockService.Setup(s => s.GetAllAsync()).ReturnsAsync(groups);

        // Act
        var result = await _controller.GetAll();

        // Assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var returnedGroups = Assert.IsType<List<AltersGruppe>>(okResult.Value);
        Assert.Equal(2, returnedGroups.Count);
    }

    [Fact]
    public async Task GetAll_EmptyList_ReturnsOkWithEmptyList()
    {
        // Arrange
        _mockService.Setup(s => s.GetAllAsync()).ReturnsAsync(new List<AltersGruppe>());

        // Act
        var result = await _controller.GetAll();

        // Assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var returnedGroups = Assert.IsType<List<AltersGruppe>>(okResult.Value);
        Assert.Empty(returnedGroups);
    }

    [Fact]
    public async Task GetOne_ExistingId_ReturnsOkWithGroup()
    {
        // Arrange
        var group = new AltersGruppe { Id = "123", Name = "U14", StartingAge = 12, EndingAge = 14 };
        _mockService.Setup(s => s.GetAsync("123")).ReturnsAsync(group);

        // Act
        var result = await _controller.GetOne("123");

        // Assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var returnedGroup = Assert.IsType<AltersGruppe>(okResult.Value);
        Assert.Equal("U14", returnedGroup.Name);
    }

    [Fact]
    public async Task GetOne_NonExistingId_ReturnsNotFound()
    {
        // Arrange
        _mockService.Setup(s => s.GetAsync("nonexistent")).ReturnsAsync((AltersGruppe?)null);

        // Act
        var result = await _controller.GetOne("nonexistent");

        // Assert
        Assert.IsType<NotFoundResult>(result);
    }

    [Fact]
    public async Task Create_ValidGroup_ReturnsCreatedAtAction()
    {
        // Arrange
        var ingoingGroup = new AltersGruppeIngoing { Name = "U16", StartingAge = 14, EndingAge = 16 };
        var createdGroup = new AltersGruppe { Id = "new-id", Name = "U16", StartingAge = 14, EndingAge = 16 };

        _mockService.Setup(s => s.CreateAsync(It.IsAny<AltersGruppe>())).ReturnsAsync(createdGroup);

        // Act
        var result = await _controller.Create(ingoingGroup);

        // Assert
        var createdResult = Assert.IsType<CreatedAtActionResult>(result);
        Assert.Equal(nameof(AgeGroupController.GetOne), createdResult.ActionName);

        var returnedGroup = Assert.IsType<AltersGruppe>(createdResult.Value);
        Assert.Equal("U16", returnedGroup.Name);
        Assert.Equal("new-id", createdResult.RouteValues?["Id"]);
    }

    [Fact]
    public async Task Update_ExistingGroup_ReturnsOkWithUpdatedGroup()
    {
        // Arrange
        var ingoingGroup = new AltersGruppeIngoing { Name = "U16 Updated", StartingAge = 15, EndingAge = 16 };
        var updatedGroup = new AltersGruppe { Id = "123", Name = "U16 Updated", StartingAge = 15, EndingAge = 16 };

        _mockService.Setup(s => s.UpdateAsync("123", It.IsAny<AltersGruppe>())).ReturnsAsync(updatedGroup);

        // Act
        var result = await _controller.Update("123", ingoingGroup);

        // Assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var returnedGroup = Assert.IsType<AltersGruppe>(okResult.Value);
        Assert.Equal("U16 Updated", returnedGroup.Name);
        Assert.Equal(15, returnedGroup.StartingAge);
    }

    [Fact]
    public async Task Update_NonExistingGroup_ReturnsNotFound()
    {
        // Arrange
        var ingoingGroup = new AltersGruppeIngoing { Name = "U16 Updated", StartingAge = 15, EndingAge = 16 };
        _mockService.Setup(s => s.UpdateAsync("nonexistent", It.IsAny<AltersGruppe>())).ReturnsAsync((AltersGruppe?)null);

        // Act
        var result = await _controller.Update("nonexistent", ingoingGroup);

        // Assert
        Assert.IsType<NotFoundResult>(result);
    }

    [Fact]
    public async Task Delete_ExistingGroup_ReturnsNoContent()
    {
        // Arrange
        var group = new AltersGruppe { Id = "123", Name = "U18", StartingAge = 16, EndingAge = 18 };
        _mockService.Setup(s => s.DeleteAsync("123")).ReturnsAsync(group);

        // Act
        var result = await _controller.Delete("123");

        // Assert
        Assert.IsType<NoContentResult>(result);
    }

    [Fact]
    public async Task Delete_NonExistingGroup_ReturnsNotFound()
    {
        // Arrange
        _mockService.Setup(s => s.DeleteAsync("nonexistent")).ReturnsAsync((AltersGruppe?)null);

        // Act
        var result = await _controller.Delete("nonexistent");

        // Assert
        Assert.IsType<NotFoundResult>(result);
    }

    [Fact]
    public async Task Create_ServiceIsCalled_WithCorrectParameters()
    {
        // Arrange
        var ingoingGroup = new AltersGruppeIngoing { Name = "U20", StartingAge = 18, EndingAge = 20 };
        var createdGroup = new AltersGruppe { Id = "new-id", Name = "U20", StartingAge = 18, EndingAge = 20 };

        _mockService.Setup(s => s.CreateAsync(It.IsAny<AltersGruppe>())).ReturnsAsync(createdGroup);

        // Act
        await _controller.Create(ingoingGroup);

        // Assert
        _mockService.Verify(s => s.CreateAsync(It.Is<AltersGruppe>(g =>
            g.Name == "U20" &&
            g.StartingAge == 18 &&
            g.EndingAge == 20
        )), Times.Once);
    }
}
