using AutoMapper;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.Interfaces;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Interfaces;

namespace SportsDivision.Application.Services;

public class RegistrationService : IRegistrationService
{
    private readonly IUnitOfWork _uow;
    private readonly IMapper _mapper;

    public RegistrationService(IUnitOfWork uow, IMapper mapper)
    {
        _uow = uow;
        _mapper = mapper;
    }

    public async Task<IEnumerable<EventRegistrationDto>> GetByTournamentEventLevelAsync(int tournamentEventLevelId)
    {
        var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tournamentEventLevelId);
        return _mapper.Map<IEnumerable<EventRegistrationDto>>(regs);
    }

    public async Task<IEnumerable<EventRegistrationDto>> GetByStudentAsync(int studentId)
    {
        var regs = await _uow.EventRegistrations.GetByStudentAsync(studentId);
        return _mapper.Map<IEnumerable<EventRegistrationDto>>(regs);
    }

    public async Task<EventRegistrationDto> RegisterStudentAsync(EventRegistrationCreateDto dto, string registeredBy)
    {
        var (isEligible, reason) = await CheckEligibilityAsync(dto.TournamentEventLevelId, dto.StudentId!.Value);
        if (!isEligible)
            throw new InvalidOperationException($"Student is not eligible: {reason}");

        var reg = new EventRegistration
        {
            TournamentEventLevelId = dto.TournamentEventLevelId,
            StudentId = dto.StudentId,
            RelayTeamId = dto.RelayTeamId,
            RegisteredBy = registeredBy,
            RegisteredAt = DateTime.UtcNow
        };

        await _uow.EventRegistrations.AddAsync(reg);
        await _uow.SaveChangesAsync();
        return _mapper.Map<EventRegistrationDto>(reg);
    }

    public async Task UnregisterAsync(int eventRegistrationId)
    {
        var reg = await _uow.EventRegistrations.GetByIdAsync(eventRegistrationId)
            ?? throw new KeyNotFoundException("Registration not found.");
        _uow.EventRegistrations.Remove(reg);
        await _uow.SaveChangesAsync();
    }

    public async Task<(bool IsEligible, string? Reason)> CheckEligibilityAsync(int tournamentEventLevelId, int studentId)
    {
        var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(tournamentEventLevelId);
        if (tel == null) return (false, "Event level not found.");

        var student = await _uow.Students.GetByIdAsync(studentId);
        if (student == null) return (false, "Student not found.");

        // Check sex match
        if (student.Sex != tel.EventLevel.Sex)
            return (false, $"Student sex ({student.Sex}) does not match event level ({tel.EventLevel.Sex}).");

        // Check school level compatibility
        var school = await _uow.Schools.GetByIdAsync(student.SchoolId);
        if (school == null) return (false, "Student's school not found.");

        bool schoolLevelMatch = tel.EventLevel.SchoolLevel == school.SchoolLevel;
        // Allow "compete up" — primary students can enter secondary events, but not the reverse
        if (!schoolLevelMatch && !(school.SchoolLevel == Domain.Enums.SchoolLevel.Primary && tel.EventLevel.SchoolLevel == Domain.Enums.SchoolLevel.Secondary))
            return (false, $"School level ({school.SchoolLevel}) is not compatible with event level ({tel.EventLevel.SchoolLevel}).");

        // Check age eligibility (if age-based and not waived)
        if (tel.EventLevel.IsAgeBased && tel.EventLevel.MaxAge.HasValue && !tel.AgeRestrictionWaived)
        {
            var referenceDate = tel.Tournament.StartDate;
            var age = student.GetAge(referenceDate);
            if (age > tel.EventLevel.MaxAge.Value)
                return (false, $"Student age ({age}) exceeds maximum ({tel.EventLevel.MaxAge.Value}) for this event level.");
        }

        // Check duplicate registration
        var alreadyRegistered = await _uow.EventRegistrations.IsStudentRegisteredAsync(tournamentEventLevelId, studentId);
        if (alreadyRegistered)
            return (false, "Student is already registered for this event level.");

        return (true, null);
    }
}
