using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Moq;
using FluentAssertions;
using RacePlannerApi.Controllers;
using RacePlannerApi.Data;
using RacePlannerApi.DTOs;
using RacePlannerApi.Models;
using backend.Tests.Utilities;

namespace backend.Tests.Controllers;

public class DashboardControllerTests : IDisposable
{
    private readonly RacePlannerDbContext _context;
    private readonly DashboardController _controller;

    public DashboardControllerTests()
    {
        var options = new DbContextOptionsBuilder<RacePlannerDbContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _context = new RacePlannerDbContext(options);
        _controller = new DashboardController(_context);
    }

    private void SetUserContext(Guid userId, string role = "Participant")
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
            new Claim(ClaimTypes.Role, role)
        };
        var identity = new ClaimsIdentity(claims, "TestAuthType");
        var principal = new ClaimsPrincipal(identity);
        _controller.ControllerContext = new ControllerContext
        {
            HttpContext = new DefaultHttpContext { User = principal }
        };
    }

    public void Dispose()
    {
        _context.Dispose();
    }

    #region Organizer Dashboard - Positive Tests

    [Fact]
    public async Task GetOrganizerDashboard_ReturnsDashboardData()
    {
        // Arrange
        var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer);
        _context.Users.Add(organizer);

        // Create events
        var pastEvent = TestDataFactory.CreateEvent(
            name: "Past Event",
            eventDate: DateTime.UtcNow.AddDays(-10),
            organizerId: organizer.Id,
            status: EventStatus.Completed);
        var draftEvent = TestDataFactory.CreateEvent(
            name: "Draft Event",
            eventDate: DateTime.UtcNow.AddDays(30),
            organizerId: organizer.Id,
            status: EventStatus.Draft);
        var publishedEvent = TestDataFactory.CreateEvent(
            name: "Published Event",
            eventDate: DateTime.UtcNow.AddDays(15),
            organizerId: organizer.Id,
            status: EventStatus.Published);
        publishedEvent.MaxParticipants = 100;

        _context.Events.AddRange(pastEvent, draftEvent, publishedEvent);
        await _context.SaveChangesAsync();

        // Add registrations with payments
        var participant1 = TestDataFactory.CreateUser(email: "p1@example.com", role: UserRole.Participant);
        var participant2 = TestDataFactory.CreateUser(email: "p2@example.com", role: UserRole.Participant);
        _context.Users.AddRange(participant1, participant2);
        await _context.SaveChangesAsync();

        var reg1 = TestDataFactory.CreateRegistration(publishedEvent.Id, participant1.Id, RegistrationStatus.Confirmed);
        var reg2 = TestDataFactory.CreateRegistration(publishedEvent.Id, participant2.Id, RegistrationStatus.Pending);
        _context.Registrations.AddRange(reg1, reg2);
        await _context.SaveChangesAsync();

        var payment = TestDataFactory.CreatePayment(reg1.Id, 50.00m, PaymentMethod.Cash);
        _context.Payments.Add(payment);
        await _context.SaveChangesAsync();

        SetUserContext(organizer.Id, "Organizer");

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

        // Assert
        var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
        var dashboard = okResult.Value.Should().BeOfType<OrganizerDashboardDto>().Subject;
        dashboard.TotalEvents.Should().Be(3);
        dashboard.PublishedEvents.Should().Be(1);
        dashboard.DraftEvents.Should().Be(1);
        dashboard.TotalRegistrations.Should().Be(2);
        dashboard.ConfirmedRegistrations.Should().Be(1);
        dashboard.PendingRegistrations.Should().Be(1);
        dashboard.TotalRevenue.Should().Be(50.00m);
        dashboard.UpcomingEvents.Should().HaveCount(2);
    }

    [Fact]
    public async Task GetOrganizerDashboard_EmptyOrganizer_ReturnsEmptyDashboard()
    {
        // Arrange
        var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer);
        _context.Users.Add(organizer);
        await _context.SaveChangesAsync();

        SetUserContext(organizer.Id, "Organizer");

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

        // Assert
        var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
        var dashboard = okResult.Value.Should().BeOfType<OrganizerDashboardDto>().Subject;
        dashboard.TotalEvents.Should().Be(0);
        dashboard.TotalRegistrations.Should().Be(0);
        dashboard.TotalRevenue.Should().Be(0);
        dashboard.UpcomingEvents.Should().BeEmpty();
    }

    #endregion

    #region Participant Dashboard - Positive Tests

    [Fact]
    public async Task GetParticipantDashboard_ReturnsDashboardData()
    {
        // Arrange
        var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer);
        var participant = TestDataFactory.CreateUser(email: "participant@example.com", role: UserRole.Participant);
        _context.Users.AddRange(organizer, participant);

        var upcomingEvent = TestDataFactory.CreateEvent(
            name: "Upcoming Event",
            eventDate: DateTime.UtcNow.AddDays(15),
            organizerId: organizer.Id,
            status: EventStatus.Published);
        var pastEvent = TestDataFactory.CreateEvent(
            name: "Past Event",
            eventDate: DateTime.UtcNow.AddDays(-10),
            organizerId: organizer.Id,
            status: EventStatus.Completed);

        var cancelledEvent = TestDataFactory.CreateEvent(name: "Cancelled Event", organizerId: organizer.Id, status: EventStatus.Published);
        _context.Events.AddRange(upcomingEvent, pastEvent, cancelledEvent);
        await _context.SaveChangesAsync();

        var upcomingReg = TestDataFactory.CreateRegistration(upcomingEvent.Id, participant.Id, RegistrationStatus.Confirmed);
        var pastReg = TestDataFactory.CreateRegistration(pastEvent.Id, participant.Id, RegistrationStatus.Completed);
        var cancelledReg = TestDataFactory.CreateRegistration(cancelledEvent.Id, participant.Id, RegistrationStatus.Cancelled);
        _context.Registrations.AddRange(upcomingReg, pastReg, cancelledReg);
        await _context.SaveChangesAsync();

        SetUserContext(participant.Id, "Participant");

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

        // Assert
        var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
        var dashboard = okResult.Value.Should().BeOfType<ParticipantDashboardDto>().Subject;
        dashboard.TotalRegistrations.Should().Be(3);
        dashboard.UpcomingEvents.Should().Be(1);
        dashboard.CompletedEvents.Should().Be(1);
        dashboard.CancelledRegistrations.Should().Be(1);
        dashboard.MyRegistrations.Should().HaveCount(3);
        dashboard.UpcomingEventList.Should().HaveCount(1);
    }

    [Fact]
    public async Task GetParticipantDashboard_NoRegistrations_ReturnsEmptyDashboard()
    {
        // Arrange
        var participant = TestDataFactory.CreateUser(role: UserRole.Participant);
        _context.Users.Add(participant);
        await _context.SaveChangesAsync();

        SetUserContext(participant.Id, "Participant");

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

        // Assert
        var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
        var dashboard = okResult.Value.Should().BeOfType<ParticipantDashboardDto>().Subject;
        dashboard.TotalRegistrations.Should().Be(0);
        dashboard.UpcomingEvents.Should().Be(0);
        dashboard.MyRegistrations.Should().BeEmpty();
    }

    #endregion

    #region Dashboard - Negative Tests

    [Fact]
    public async Task GetOrganizerDashboard_Unauthenticated_ReturnsUnauthorized()
    {
        // Arrange
        _controller.ControllerContext = new ControllerContext
        {
            HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() }
        };

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

        // Assert
        result.Result.Should().BeOfType<UnauthorizedResult>();
    }

    [Fact]
    public async Task GetParticipantDashboard_Unauthenticated_ReturnsUnauthorized()
    {
        // Arrange
        _controller.ControllerContext = new ControllerContext
        {
            HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() }
        };

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

        // Assert
        result.Result.Should().BeOfType<UnauthorizedResult>();
    }

    #endregion

    #region Event Capacity Tests

    [Fact]
    public async Task GetOrganizerDashboard_ShowsEventsNearCapacity()
    {
        // Arrange
        var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer);
        _context.Users.Add(organizer);

        // Event at 90% capacity (should appear in near capacity list)
        var nearCapacityEvent = TestDataFactory.CreateEvent(
            name: "Near Capacity",
            eventDate: DateTime.UtcNow.AddDays(10),
            organizerId: organizer.Id,
            status: EventStatus.Published);
        nearCapacityEvent.MaxParticipants = 10;

        // Event at 50% capacity (should NOT appear)
        var normalEvent = TestDataFactory.CreateEvent(
            name: "Normal",
            eventDate: DateTime.UtcNow.AddDays(10),
            organizerId: organizer.Id,
            status: EventStatus.Published);
        normalEvent.MaxParticipants = 100;

        _context.Events.AddRange(nearCapacityEvent, normalEvent);
        await _context.SaveChangesAsync();

        // Add registrations
        var participant1 = TestDataFactory.CreateUser(email: "p1@test.com", role: UserRole.Participant);
        var participant2 = TestDataFactory.CreateUser(email: "p2@test.com", role: UserRole.Participant);
        _context.Users.AddRange(participant1, participant2);
        await _context.SaveChangesAsync();

        // Fill near capacity event to 90%
        for (int i = 0; i < 9; i++)
        {
            var p = TestDataFactory.CreateUser(email: $"test{i}@test.com", role: UserRole.Participant);
            _context.Users.Add(p);
            var reg = TestDataFactory.CreateRegistration(nearCapacityEvent.Id, p.Id, RegistrationStatus.Confirmed);
            _context.Registrations.Add(reg);
        }

        // Fill normal event to 50%
        for (int i = 0; i < 50; i++)
        {
            var p = TestDataFactory.CreateUser(email: $"normal{i}@test.com", role: UserRole.Participant);
            _context.Users.Add(p);
            var reg = TestDataFactory.CreateRegistration(normalEvent.Id, p.Id, RegistrationStatus.Confirmed);
            _context.Registrations.Add(reg);
        }
        await _context.SaveChangesAsync();

        SetUserContext(organizer.Id, "Organizer");

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

        // Assert
        var okResult = result.Result.Should().BeOfType<OkObjectResult>().Subject;
        var dashboard = okResult.Value.Should().BeOfType<OrganizerDashboardDto>().Subject;
        dashboard.EventsNearCapacity.Should().HaveCount(1);
        dashboard.EventsNearCapacity.First().Name.Should().Be("Near Capacity");
    }

    #endregion
}
