using Microsoft.EntityFrameworkCore;
using WebApp.Data;
using WebApp.DTOs;
using WebApp.Entities;

namespace WebApp.Endpoints;

public class GeneralUseHelpers(ApplicationDbContext context)
{

    private readonly ApplicationDbContext _context = context;

    async public Task<Token?> FindTokenFromString(string token_str)
    {
        // foreach (Token t in _context.Tokens) if (t.Value == token) return t;
        // return null;
        return await _context.Tokens.FirstOrDefaultAsync(t => t.Value == token_str);
    }

    async public Task<User?> GetUserFromToken(Token? t)
    {
        // Zwróci null, gdy nie znaleziono użytkownika
        if (t is null) return null;
        User? user = await _context.WebUsers.FindAsync(t.UserId);

        return user;
    }

    async public Task<Organisation?> GetOrganisationFromToken(Token? t)
    {
        User? user = await GetUserFromToken(t);
        if (user is not null && user.IsOrganisation)
        {
            Organisation? org = await _context.Organisations.FirstOrDefaultAsync(o => o.UserId == t!.UserId);

            return org;
        }
        else return null;

    }

    async public Task<Organisation?> GetOrganisationFromId(int id)
    {
        Organisation? org = await _context.Organisations.FirstOrDefaultAsync(o => o.OrganisationId == id);
        return org;
    }

    async public Task<Organisation?> GetOrganisationFromUserId(int userId)
    {
        Organisation? org = await _context.Organisations.FirstOrDefaultAsync(o => o.UserId == userId);
        return org;
    }

    public string? GetTokenStrFromHTTPContext(HttpContext httpContext)
    {
        var cookies = httpContext.Request.Cookies;
        string? token = cookies["token"];
        return token;
    }

    async public Task<Token?> GetTokenFromHTTPContext(HttpContext httpContext)
    {
        var cookies = httpContext.Request.Cookies;
        string? token_str = cookies["token"];
        if (token_str is not null)
        {
            Token? token = await FindTokenFromString(token_str);
            if (token is not null) return token;
        }
        return null;
    }

    public async Task<Token> CreateNewToken(int userId)
    {
        var token = new Token
        {
            UserId = userId,
            Value = "lah-" + Guid.NewGuid().ToString(),
            ValidUntil = DateTime.UtcNow.AddDays(7)
        };

        _context.Tokens.Add(token);
        await _context.SaveChangesAsync();

        return token;
    }

    public async Task DeleteToken(Token token)
    {
        _context.Tokens.Remove(token);
        await _context.SaveChangesAsync();
    }

    public bool SearchString(string? text, string searchTerm)
    {
        // Zwraca fałsz jeśli tekst jest pusty.
        // (Brak tekstu nie wpływa na wynik wyszukiwania).
        if (text is null) return false;

        // Zamienia tekst na słowa
        var words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);

        // Sprawdza, czy któreś ze słów pasuje (nawet częściowo) do searchTerm
        return words.Any(word => word.Contains(searchTerm, StringComparison.OrdinalIgnoreCase));
    }

    public async Task<List<EventDetailsDto>> BuildDetailedEventsDto(
        ApplicationDbContext context,
        Organisation? org,
        bool sortAscending = false)
    {
        // https://khalidabuhakmeh.com/ef-core-and-aspnet-core-cycle-issue-and-solution

        // Jeśli token należy do organizacji, która utworzyła to wydarzenie,
        // to zwróć także EventRegistrations. W przeciwnym razie niech będzie to
        // puste pole.

        IQueryable<EventDetailsDto> result_iq = context
                .Events
                .Select(e => new EventDetailsDto
                {
                    EventId = e.EventId,
                    OrganisationId = e.OrganisationId,
                    OrganisationName = e.Organisation!.Name,
                    Title = e.Title,
                    Description = e.Description ?? "",
                    ImageURL = e.ImageURL,
                    Location = e.Location,
                    EventDate = e.EventDate,
                    EventSkills = e
                        .EventSkills
                        .Select(es => new SkillSummaryDto
                        {
                            SkillId = es.SkillId,
                            SkillName = es.Skill!.Name
                        }).ToList(),
                    EventRegistrations = e.Organisation == org ?
                        e.EventRegistrations
                        .Select(er => new EventRegistrationDto
                        {
                            EventId = er.EventId,
                            UserId = er.UserId,
                            UserName = er.User!.FirstName + " " + er.User.LastName,
                            RegisteredAt = er.RegisteredAt
                        }).ToList() : null!
                });

        if (sortAscending) result_iq = result_iq.OrderBy(e => e.EventId);
        else               result_iq = result_iq.OrderByDescending(e => e.EventId);


        return await result_iq.ToListAsync();
    }

    public async Task<List<EventSummaryDto>> BuildSummaryEventsDto(
        ApplicationDbContext context,
        Organisation? org,
        bool sortAscending = false)
    {
        // https://khalidabuhakmeh.com/ef-core-and-aspnet-core-cycle-issue-and-solution

        // Jeśli token należy do organizacji, która utworzyła to wydarzenie,
        // to zwróć także EventRegistrations. W przeciwnym razie niech będzie to
        // puste pole.
        IQueryable<EventSummaryDto> result_iq = context
                .Events
                .Select(e => new EventSummaryDto
                {
                    EventId = e.EventId,
                    OrganisationId = e.OrganisationId,
                    Organisation = e.Organisation!.Name,
                    Title = e.Title,
                    Description = e.Description ?? "",
                    ImageURL = e.ImageURL,
                    Location = e.Location,
                    EventDate = e.EventDate,
                    EventSkills = e
                        .EventSkills
                        .Select(es => new SkillSummaryDto
                        {
                            SkillId = es.SkillId,
                            SkillName = es.Skill!.Name
                        }).ToList(),
                    EventRegistrations = e.Organisation == org ?
                        e.EventRegistrations
                        .Select(er => new EventRegistrationDto
                        {
                            EventId = er.EventId,
                            UserId = er.UserId,
                            UserName = er.User!.FirstName + " " + er.User.LastName,
                            RegisteredAt = er.RegisteredAt
                        }).ToList() : null!
                });

        if (sortAscending) result_iq = result_iq.OrderBy(e => e.EventId);
        else               result_iq = result_iq.OrderByDescending(e => e.EventId);

        return await result_iq.ToListAsync();
    }

}
