using Api.Database;
using Api.Database.Entities;
using Api.Extensions;
using Api.Mapping;
using Api.Services.Interfaces;
using Common.Dtos.Common;
using Common.Dtos.Season;
using Common.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;

namespace Api.Services.Implementation;

public class SeasonService(SanStudentContext context, IDistributedCache cache) : ISeasonService, IServiceHelper<CreateSeasonRequest>
{
    private readonly SanStudentContext _context = context;
    private readonly IDistributedCache _cache = cache;
    private List<string> _cacheKeys = [];

    public async Task<CreateSeasonResponse> CreateAsync(CreateSeasonRequest request)
    {
        try
        {
            var problems = await Validate(request);
            if (problems.Errors.Count > 0)
            {
                return new CreateSeasonResponse
                {
                    Ok = false,
                    CreateSeasonResult = string.Join(", ", problems.Errors)
                };
            }

            var season = new Season()
            {
                Name = request.Name!.Trim(),
                StartDate = request.StartDate,
                EndDate = request.EndDate
            };

            _context.Seasons.Add(season);
            await _context.SaveChangesAsync();
            await RemoveCache();
            return new CreateSeasonResponse
            {
                Ok = true,
                CreateSeasonResult = $"Zapisano nowy semestr \"{season.Name}\"",
            };
        }
        catch (Exception error)
        {
            return new CreateSeasonResponse
            {
                Ok = false,
                CreateSeasonResult = $"Błąd podczas próby zapisu semestru: {error.Message}",
            };
        }
    }

    public async Task<SeasonDto[]> GetAllAsync()
    {
        var key = "seasons";
        AddKeyToCache(key);
        var cachedSeasons = await _cache.GetStringAsync(key);
        if (!string.IsNullOrEmpty(cachedSeasons))
            return cachedSeasons.FromJson<SeasonDto[]>();

        var query = _context.Seasons as IQueryable<Season>;
        var result = await query
            .Where(s => s.EndDate > DateOnly.FromDateTime(DateTime.Now))
            .OrderBy(s => s.StartDate)
            .ThenBy(s => s.Name)
            .Select(s => s.ToSeasonDto()).ToArrayAsync();

        await _cache.SetWithExpirationTimeAsync(key, result, 131487);
        return result;
    }

    public async Task<PagedList<SeasonDto>> GetAllPagedAsync(GetSeasonsRequest request)
    {
        var key = $"seasons_{request.PageNumber}_{request.PageSize}";
        if (!string.IsNullOrWhiteSpace(request.Name))
            key += $"_{request.Name.ToLower()}";

        if (request.HideObsolete.HasValue && request.HideObsolete.Value)
            key += $"_{true}";

        AddKeyToCache(key);
        var cachedSeasons = await _cache.GetStringAsync(key);
        if (!string.IsNullOrEmpty(cachedSeasons))
            return cachedSeasons.FromJson<PagedList<SeasonDto>>();

        var query = _context.Seasons as IQueryable<Season>;

        if (!string.IsNullOrEmpty(request.Name))
            query = query.Where(s => s.Name.ToLower().StartsWith(request.Name.ToLower()));

        if (request.HideObsolete.HasValue && request.HideObsolete.Value)
            query = query.Where(s => s.EndDate > DateOnly.FromDateTime(DateTime.Now));

        var data = query
            .OrderBy(s => s.StartDate)
            .ThenBy(s => s.Name)
            .Select(s => s.ToSeasonDto());

        var result = await data.ToPagedListAsync(request.PageNumber, request.PageSize);
        await _cache.SetWithExpirationTimeAsync(key, result, 131487);
        return result;

    }

    public async Task<ValidationProblems> Validate(CreateSeasonRequest request)
    {
        var problems = new ValidationProblems();

        if (string.IsNullOrWhiteSpace(request.Name))
            problems.Errors.Add("Brak nazwy semestru");

        if (problems.Errors.Count > 0)
            return problems;


        var season = await _context.Seasons
                .FirstOrDefaultAsync(s => s.Name.ToLower() == request.Name!.ToLower());

        if (season is not null)
            problems.Errors.Add("Semestr o podanej nazwie już istnieje");

        season = await _context.Seasons
            .FirstOrDefaultAsync(s => s.StartDate == request.StartDate);

        if (season is not null)
            problems.Errors.Add("Semestr o podanej dacie rozpoczęcia już istnieje");

        season = await _context.Seasons
            .FirstOrDefaultAsync(s => s.EndDate == request.EndDate);

        if (season is not null)
            problems.Errors.Add("Semestr o podanej dacie zakończenia już istnieje");

        return problems;
    }

    public async Task RemoveCache()
    {
        foreach (var cacheKey in _cacheKeys)
            await _cache.RemoveAsync(cacheKey);
    }

    public void AddKeyToCache(string key)
    {
        if (!_cacheKeys.Contains(key))
            _cacheKeys.Add(key);
    }
}

