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

namespace SportsDivision.Application.Services;

public class TournamentService : ITournamentService
{
    private readonly IUnitOfWork _uow;
    private readonly IMapper _mapper;

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

    public async Task<IEnumerable<TournamentDto>> GetAllAsync(bool includeArchived = false)
    {
        var tournaments = includeArchived
            ? await _uow.Tournaments.GetAllAsync()
            : await _uow.Tournaments.GetActiveAsync();
        return _mapper.Map<IEnumerable<TournamentDto>>(tournaments);
    }

    public async Task<TournamentDto?> GetByIdAsync(int id)
    {
        var tournament = await _uow.Tournaments.GetWithEventLevelsAsync(id);
        return tournament == null ? null : _mapper.Map<TournamentDto>(tournament);
    }

    public async Task<TournamentDto> CreateAsync(TournamentCreateDto dto)
    {
        var tournament = _mapper.Map<Tournament>(dto);
        await _uow.Tournaments.AddAsync(tournament);
        await _uow.SaveChangesAsync();
        return _mapper.Map<TournamentDto>(tournament);
    }

    public async Task UpdateAsync(TournamentUpdateDto dto)
    {
        var tournament = await _uow.Tournaments.GetByIdAsync(dto.TournamentId)
            ?? throw new KeyNotFoundException("Tournament not found.");
        _mapper.Map(dto, tournament);
        _uow.Tournaments.Update(tournament);
        await _uow.SaveChangesAsync();
    }

    public async Task UpdateStatusAsync(int id, TournamentStatus status)
    {
        var tournament = await _uow.Tournaments.GetByIdAsync(id)
            ?? throw new KeyNotFoundException("Tournament not found.");
        tournament.Status = status;
        _uow.Tournaments.Update(tournament);
        await _uow.SaveChangesAsync();
    }

    public async Task ArchiveAsync(int id)
    {
        var tournament = await _uow.Tournaments.GetByIdAsync(id)
            ?? throw new KeyNotFoundException("Tournament not found.");
        if (tournament.Status != TournamentStatus.Completed)
            throw new InvalidOperationException("Only tournaments marked as Completed can be archived.");
        tournament.IsArchived = true;
        _uow.Tournaments.Update(tournament);
        await _uow.SaveChangesAsync();
    }

    public async Task UnarchiveAsync(int id)
    {
        var tournament = await _uow.Tournaments.GetByIdAsync(id)
            ?? throw new KeyNotFoundException("Tournament not found.");
        tournament.IsArchived = false;
        _uow.Tournaments.Update(tournament);
        await _uow.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var tournament = await _uow.Tournaments.GetByIdAsync(id)
            ?? throw new KeyNotFoundException("Tournament not found.");
        _uow.Tournaments.Remove(tournament);
        await _uow.SaveChangesAsync();
    }

    public async Task<IEnumerable<TournamentEventLevelDto>> GetEventLevelsAsync(int tournamentId)
    {
        var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId);
        return _mapper.Map<IEnumerable<TournamentEventLevelDto>>(tels);
    }

    public async Task<TournamentEventLevelDto?> GetEventLevelByIdAsync(int tournamentEventLevelId)
    {
        var tel = await _uow.TournamentEventLevels.GetWithRegistrationsAsync(tournamentEventLevelId);
        return tel == null ? null : _mapper.Map<TournamentEventLevelDto>(tel);
    }

    public async Task<IEnumerable<EventLevelDto>> GetAllEventLevelsAsync()
    {
        var levels = await _uow.EventLevels.GetAllAsync();
        return _mapper.Map<IEnumerable<EventLevelDto>>(levels.OrderBy(l => l.SortOrder));
    }

    public async Task<IEnumerable<TournamentEventLevelDto>> GetEventLevelsByCategoryAsync(EventCategory category)
    {
        var tournaments = await _uow.Tournaments.GetActiveAsync();
        var result = new List<TournamentEventLevelDto>();
        foreach (var t in tournaments)
        {
            var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(t.TournamentId);
            foreach (var tel in tels.Where(x => x.Event != null && x.Event.Category == category))
            {
                var dto = _mapper.Map<TournamentEventLevelDto>(tel);
                dto.TournamentName = t.Name;
                result.Add(dto);
            }
        }
        return result;
    }

    public async Task<TournamentEventLevelDto> AddEventLevelAsync(TournamentEventLevelCreateDto dto)
    {
        var existing = await _uow.TournamentEventLevels.FindAsync(dto.TournamentId, dto.EventId, dto.EventLevelId);
        if (existing != null)
            throw new InvalidOperationException("This event/level combination already exists for this tournament.");

        var tel = _mapper.Map<TournamentEventLevel>(dto);
        await _uow.TournamentEventLevels.AddAsync(tel);
        await _uow.SaveChangesAsync();
        return _mapper.Map<TournamentEventLevelDto>(tel);
    }

    public async Task RemoveEventLevelAsync(int tournamentEventLevelId)
    {
        var tel = await _uow.TournamentEventLevels.GetByIdAsync(tournamentEventLevelId)
            ?? throw new KeyNotFoundException("Tournament event level not found.");
        _uow.TournamentEventLevels.Remove(tel);
        await _uow.SaveChangesAsync();
    }

    public async Task ToggleAgeWaiverAsync(int tournamentEventLevelId)
    {
        var tel = await _uow.TournamentEventLevels.GetByIdAsync(tournamentEventLevelId)
            ?? throw new KeyNotFoundException("Tournament event level not found.");
        tel.AgeRestrictionWaived = !tel.AgeRestrictionWaived;
        _uow.TournamentEventLevels.Update(tel);
        await _uow.SaveChangesAsync();
    }
}
