namespace Tests;
using Models.Game;
using Models.Rules;

public class RulesTests
{
    [Fact]
    public void IsCellEmpty_ReturnsTrue_WhenCellIsNull()
    {
        Rules rules = new Rules();
        Assert.True(rules.IsCellEmpty(null));
    }
    
    [Fact]
    public void IsCellEmpty_ReturnsTrue_WhenCellValueIsNull()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        Assert.True(rules.IsCellEmpty(cell));
    }
    
    [Fact]
    public void IsCellEmpty_ReturnsFalse_WhenCellValueIsNotNull()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        Assert.False(rules.IsCellEmpty(cell));
    }
    
    [Fact]
    public void IsCellValid_ReturnsFalse_WhenCellIsNotEmptyAndHasAdjacentCells()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell selectedCell = map.Boards[0];
        selectedCell.Value = 5;
        Assert.False(rules.IsCellValid(selectedCell, map.Boards.ToList()));
    }
    
    [Fact]
    public void IsCellValid_ReturnsTrue_WhenCellIsEmptyAndHasAdjacentCells()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell selectedCell = map.Boards[0];
        Assert.True(rules.IsCellValid(selectedCell, map.Boards.ToList()));
    }
    
    [Fact]
    public void IsCellAdjacent_ReturnsFalse_WhenCellsAreNotAdjacent()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(2, 2);
        Assert.False(rules.IsCellAdjacent(cell1, cell2));
    }
    
    [Fact]
    public void IsCellAdjacent_ReturnsTrue_WhenCellsAreSideBySide()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        Assert.True(rules.IsCellAdjacent(cell1, cell2));
    }
    
    [Fact]
    public void IsCellAdjacent_ReturnsTrue_WhenCellsAreDiagonal()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(1, 1);
        Assert.True(rules.IsCellAdjacent(cell1, cell2));
    }
    
    [Fact]
    public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePaths()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1, cell2 });
        Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void IsInRopePaths_ReturnsFalse_WhenCellsAreNotInRopePaths()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1 });
        Assert.False(rules.IsInRopePaths(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePathsButNotInTheSamePath()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1 });
        map.RopePaths.Add(new List<Cell> { cell2 });
        Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void AsValue_ReturnsTrue_WhenCellHasTheSameValue()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        cell1.Value = 5;
        cell2.Value = 5;
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1, cell2 });
        Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void AsValue_ReturnsTrue_WhenCellValueIsAlreadyInRopePaths()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        cell1.Value = 5;
        cell2.Value = 5;
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1 });
        Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void AsValue_ReturnsFalse_WhenCellHasDifferentValue()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        cell1.Value = 5;
        cell2.Value = 6;
        Map map = new Map("background");
        map.RopePaths.Add(new List<Cell> { cell1 });
        Assert.False(rules.AsValue(cell2, map.RopePaths, 0));
    }
    
    [Fact]
    public void NearCellIsValid_ReturnsFalse_WhenChosenCellIsNull()
    {
        Rules rules = new Rules();
        Assert.False(rules.NearCellIsValid(null, new List<Cell>()));
    }

    [Fact]
    public void NearCellIsValid_ReturnsFalse_WhenCellsIsNull()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        Assert.False(rules.NearCellIsValid(cell, null));
    }

    [Fact]
    public void NearCellIsValid_ReturnsFalse_WhenNoAdjacentCells()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<Cell> cells = new List<Cell> { new Cell(2, 0) };
        Assert.False(rules.NearCellIsValid(cell, cells));
    }

    [Fact]
    public void NearCellIsValid_ReturnsTrue_WhenAdjacentCellExists()
    {
        Rules rules = new Rules();
        Cell cell1 = new Cell(0, 0);
        Cell cell2 = new Cell(0, 1);
        cell2.Value = 12;
        List<Cell> cells = new List<Cell> { cell2 };
        Assert.True(rules.NearCellIsValid(cell1, cells));
    }
    
    [Fact]
    public void IsZoneValidAndAddToZones_DoesNothing_WhenCellIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        rules.IsZoneValidAndAddToZones(null, map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void IsZoneValidAndAddToZones_DoesNothing_WhenCellValueIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell cell = new Cell(0, 0);
        rules.IsZoneValidAndAddToZones(cell, map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void IsZoneValidAndAddToZones_AddsToExistingZone_WhenAdjacentCellWithSameValueExists()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        Cell adjacentCell = new Cell(0, 1);
        adjacentCell.Value = 1;
        
        map.Boards.ToList().Add(adjacentCell);
        map.Zones.Add(new List<Cell> { adjacentCell });
        
        rules.IsZoneValidAndAddToZones(cell, map);
        Assert.Contains(cell, map.Zones[0]);
    }

    [Fact]
    public void IsZoneValidAndAddToZones_CreatesNewZone_WhenAdjacentCellWithSameValueExistsButNoExistingZone()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        Cell adjacentCell = new Cell(0, 1);
        adjacentCell.Value = 1;
        
        map.Boards.ToList().Add(adjacentCell);
        rules.IsZoneValidAndAddToZones(cell, map);
        Assert.Contains(cell, map.Zones[0]);
    }
    
    [Fact]
    public void IsValueInZones_ReturnsFalse_WhenCellIsNull()
    {
        Rules rules = new Rules();
        Assert.False(rules.IsValueInZones(null, new List<List<Cell>>()));
    }

    [Fact]
    public void IsValueInZones_ReturnsFalse_WhenZonesAreEmpty()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        Assert.False(rules.IsValueInZones(cell, new List<List<Cell>>()));
    }

    [Fact]
    public void IsValueInZones_ReturnsFalse_WhenNoZoneContainsCellWithSameValue()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { new Cell(0, 1) { Value = 2 } },
            new List<Cell> { new Cell(1, 0) { Value = 3 } }
        };
        Assert.False(rules.IsValueInZones(cell, zones));
    }

    [Fact]
    public void IsValueInZones_ReturnsTrue_WhenZoneContainsCellWithSameValue()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { new Cell(0, 1) { Value = 1 } },
            new List<Cell> { new Cell(1, 0) { Value = 3 } }
        };
        Assert.True(rules.IsValueInZones(cell, zones));
    }
    
    [Fact]
    public void IsCellInZone_ReturnsFalse_WhenCellIsNull()
    {
        Rules rules = new Rules();
        Assert.False(rules.IsCellInZone(null, new List<List<Cell>>()));
    }

    [Fact]
    public void IsCellInZone_ReturnsFalse_WhenZonesAreEmpty()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        Assert.False(rules.IsCellInZone(cell, new List<List<Cell>>()));
    }

    [Fact]
    public void IsCellInZone_ReturnsFalse_WhenNoZoneContainsCell()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { new Cell(0, 1) },
            new List<Cell> { new Cell(1, 0) }
        };
        Assert.False(rules.IsCellInZone(cell, zones));
    }

    [Fact]
    public void IsCellInZone_ReturnsTrue_WhenZoneContainsCell()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { new Cell(0, 1) },
            new List<Cell> { cell }
        };
        Assert.True(rules.IsCellInZone(cell, zones));
    }
    
    [Fact]
    public void AddToZone_DoesNothing_WhenCellIsNull()
    {
        Rules rules = new Rules();
        List<List<Cell>> zones = new List<List<Cell>>();
        rules.AddToZone(null, zones);
        Assert.Empty(zones);
    }

    [Fact]
    public void AddToZone_DoesNothing_WhenCellValueIsNull()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<List<Cell>> zones = new List<List<Cell>>();
        rules.AddToZone(cell, zones);
        Assert.Empty(zones);
    }

    [Fact]
    public void AddToZone_DoesNothing_WhenCellIsAlreadyInZone()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { cell }
        };
        rules.AddToZone(cell, zones);
        Assert.Single(zones[0]); // Verify if the List has still only one element
    }

    [Fact]
    public void AddToZone_AddsCellToExistingZone_WhenCellHasSameValueAsZone()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { new Cell(0, 1) { Value = 1 } }
        };
        rules.AddToZone(cell, zones);
        Assert.Contains(cell, zones[0]);
    }
    
    [Fact]
    public void NewZoneIsCreated_DoesNothing_WhenFirstCellIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        rules.NewZoneIsCreated(null, new Cell(0, 0), map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void NewZoneIsCreated_DoesNothing_WhenSecondCellIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        rules.NewZoneIsCreated(new Cell(0, 0), null, map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void NewZoneIsCreated_DoesNothing_WhenFirstCellValueIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell firstCell = new Cell(0, 0);
        rules.NewZoneIsCreated(firstCell, new Cell(0, 1), map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void NewZoneIsCreated_DoesNothing_WhenSecondCellValueIsNull()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell secondCell = new Cell(0, 0);
        rules.NewZoneIsCreated(new Cell(0, 1), secondCell, map);
        Assert.Empty(map.Zones);
    }

    [Fact]
    public void NewZoneIsCreated_CreatesNewZone_WhenBothCellsAreNotNullAndHaveValues()
    {
        Rules rules = new Rules();
        Map map = new Map("background");
        Cell firstCell = new Cell(0, 0);
        firstCell.Value = 1;
        Cell secondCell = new Cell(0, 1);
        secondCell.Value = 1;
        rules.NewZoneIsCreated(firstCell, secondCell, map);
        Assert.Single(map.Zones);
        Assert.Contains(firstCell, map.Zones[0]);
        Assert.Contains(secondCell, map.Zones[0]);
    }
    
    [Fact]
    public void EveryAdjacentCells_ReturnsEmptyList_WhenCellIsNull()
    {
        Rules rules = new Rules();
        List<Cell> cells = new List<Cell> { new Cell(0, 0), new Cell(0, 1) };
        Assert.Empty(rules.EveryAdjacentCells(null, cells));
    }

    [Fact]
    public void EveryAdjacentCells_ReturnsEmptyList_WhenCellsAreEmpty()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        Assert.Empty(rules.EveryAdjacentCells(cell, new List<Cell>()));
    }

    [Fact]
    public void EveryAdjacentCells_ReturnsEmptyList_WhenNoAdjacentCells()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<Cell> cells = new List<Cell> { new Cell(2, 2), new Cell(3, 3) };
        Assert.Empty(rules.EveryAdjacentCells(cell, cells));
    }

    [Fact]
    public void EveryAdjacentCells_ReturnsListWithAdjacentCells_WhenAdjacentCellsExist()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        List<Cell> cells = new List<Cell> { new Cell(0, 1), new Cell(1, 0), new Cell(2, 2) };
        List<Cell> result = rules.EveryAdjacentCells(cell, cells);
        Assert.Equal(2, result.Count);
        Assert.Contains(new Cell(0, 1), result);
        Assert.Contains(new Cell(1, 0), result);
    }
    
    [Fact]
    public void FinalCalculusOfZones_ReturnsZero_WhenZonesAreEmpty()
    {
        Rules rules = new Rules();
        Assert.Equal(0, rules.FinalCalculusOfZones(new List<List<Cell>>()));
    }

    [Fact]
    public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZonesAreNotEmpty()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 1;
        Cell cell1 = new Cell(0, 1);
        cell1.Value = 1;
        Cell cell2 = new Cell(0, 2);
        cell2.Value = 1;
        Cell uncell = new Cell(0, 0);
        uncell.Value = 4;
        Cell deuxcell = new Cell(0, 1);
        deuxcell.Value = 4;
        Cell troiscell = new Cell(0, 2);
        troiscell.Value = 4;
        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { cell, cell1, cell2 },
            new List<Cell> { uncell, deuxcell, troiscell }
        };

        Assert.Equal(9, rules.FinalCalculusOfZones(zones));
    }
    
    [Fact]
    public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZoneCountIsGreaterThanNine()
    {
        Rules rules = new Rules();
        Cell cell = new Cell(0, 0);
        cell.Value = 5;
        Cell cell1 = new Cell(0, 1);
        cell1.Value = 5;
        Cell cell2 = new Cell(0, 2);
        cell2.Value = 5;
        Cell cell3 = new Cell(0, 3);
        cell3.Value = 5;
        Cell cell4 = new Cell(0, 4);
        cell4.Value = 5;
        Cell cell5 = new Cell(0, 5);
        cell5.Value = 5;
        Cell cell6 = new Cell(1, 0);
        cell6.Value = 5;
        Cell cell7 = new Cell(1, 1);
        cell7.Value = 5;
        Cell cell8 = new Cell(1, 2);
        cell8.Value = 5;
        Cell cell9 = new Cell(1, 3);
        cell9.Value = 5;
        Cell cell10 = new Cell(1, 4);
        cell10.Value = 5;

        List<List<Cell>> zones = new List<List<Cell>>
        {
            new List<Cell> { cell, cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8, cell9, cell10 }
        };

        Assert.Equal(23, rules.FinalCalculusOfZones(zones));
    }
    
}