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.Dtos.Specialization;
using Common.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;

namespace Api.Services.Implementation;

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

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

            var specialization = new Specialization()
            {
                Name = request.Name!.Trim(),
                ShortName = request.ShortName!.Trim()
            };

            _context.Specializations.Add(specialization);
            await _context.SaveChangesAsync();
            await RemoveCache();
            return new CreateSpecializationResponse
            {
                Ok = true,
                CreateSpecializationResult = $"Zapisano nową specializację \"{specialization.Name}\"",
            };
        }
        catch (Exception error)
        {
            return new CreateSpecializationResponse
            {
                Ok = false,
                CreateSpecializationResult = $"Błąd podczas próby zapisu specializacji: {error.Message}",
            };
        }
    }

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

        var query = _context.Specializations as IQueryable<Specialization>;
        var result = await query
            .OrderBy(s => s.Name)
            .ThenBy(s => s.ShortName)
            .Select(s => s.ToSpecializationDto()).ToArrayAsync();

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

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

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

        var query = _context.Specializations as IQueryable<Specialization>;

        if (!string.IsNullOrEmpty(request.Name))
            query = query.Where(s => s.Name.ToLower().StartsWith(request.Name.ToLower()));
        
        var data = query
            .OrderBy(s => s.Name)
            .ThenBy(s => s.ShortName)
            .Select(s => s.ToSpecializationDto());

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

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

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

        if (string.IsNullOrWhiteSpace(request.ShortName))
            problems.Errors.Add("Brak nazwy skróconej specializacji");

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


        var specialization = await _context.Specializations
            .FirstOrDefaultAsync(s => s.Name.ToLower() == request.Name!.ToLower());

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

        specialization = await _context.Specializations
            .FirstOrDefaultAsync(s => s.ShortName.ToLower() == request.ShortName!.ToLower());

        if (specialization is not null)
            problems.Errors.Add("Specializacja o podanej nazwie skróconej 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);
    }
}
