using Microsoft.EntityFrameworkCore;
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
using ColaFlow.Modules.ProjectManagement.Domain.Repositories;
using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects;
using ColaFlow.Modules.ProjectManagement.Infrastructure.Persistence;

namespace ColaFlow.Modules.ProjectManagement.Infrastructure.Repositories;

/// <summary>
/// Project repository implementation using EF Core
/// </summary>
public class ProjectRepository(PMDbContext context) : IProjectRepository
{
    private readonly PMDbContext _context = context ?? throw new ArgumentNullException(nameof(context));

    // ========== Basic CRUD Operations ==========

    public async Task<Project?> GetByIdAsync(ProjectId id, CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .Include(p => p.Epics)
            .FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
    }

    public async Task<Project?> GetByKeyAsync(string key, CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .FirstOrDefaultAsync(p => p.Key.Value == key, cancellationToken);
    }

    public async Task<List<Project>> GetAllAsync(CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .OrderByDescending(p => p.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    public async Task AddAsync(Project project, CancellationToken cancellationToken = default)
    {
        await _context.Projects.AddAsync(project, cancellationToken);
    }

    public void Update(Project project)
    {
        _context.Projects.Update(project);
    }

    public void Delete(Project project)
    {
        _context.Projects.Remove(project);
    }

    // ========== Aggregate Root Loading (for Command Handlers - with tracking) ==========

    public async Task<Project?> GetProjectWithEpicAsync(EpicId epicId, CancellationToken cancellationToken = default)
    {
        // Load only the specific Epic with its Stories (filtered Include)
        return await _context.Projects
            .Include(p => p.Epics.Where(e => e.Id == epicId))
                .ThenInclude(e => e.Stories)
            .Where(p => p.Epics.Any(e => e.Id == epicId))
            .FirstOrDefaultAsync(cancellationToken);
    }

    public async Task<Project?> GetProjectWithStoryAsync(StoryId storyId, CancellationToken cancellationToken = default)
    {
        // Load the Epic containing the Story, with that Story and its Tasks
        return await _context.Projects
            .Include(p => p.Epics)
                .ThenInclude(e => e.Stories.Where(s => s.Id == storyId))
                    .ThenInclude(s => s.Tasks)
            .Where(p => p.Epics.Any(e => e.Stories.Any(s => s.Id == storyId)))
            .FirstOrDefaultAsync(cancellationToken);
    }

    public async Task<Project?> GetProjectWithTaskAsync(TaskId taskId, CancellationToken cancellationToken = default)
    {
        // Load the Epic and Story containing the Task, with that Task
        return await _context.Projects
            .Include(p => p.Epics)
                .ThenInclude(e => e.Stories)
                    .ThenInclude(s => s.Tasks.Where(t => t.Id == taskId))
            .Where(p => p.Epics.Any(e => e.Stories.Any(s => s.Tasks.Any(t => t.Id == taskId))))
            .FirstOrDefaultAsync(cancellationToken);
    }

    public async Task<Project?> GetProjectWithEpicsAsync(ProjectId projectId, CancellationToken cancellationToken = default)
    {
        // Load Project with all Epics, but without Stories and Tasks
        return await _context.Projects
            .Include(p => p.Epics)
            .FirstOrDefaultAsync(p => p.Id == projectId, cancellationToken);
    }

    // ========== Read-Only Queries (for Query Handlers - AsNoTracking) ==========

    public async Task<Epic?> GetEpicByIdReadOnlyAsync(EpicId epicId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<Epic>()
            .AsNoTracking()
            .Include(e => e.Stories)
                .ThenInclude(s => s.Tasks)
            .FirstOrDefaultAsync(e => e.Id == epicId, cancellationToken);
    }

    public async Task<List<Epic>> GetEpicsByProjectIdAsync(ProjectId projectId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<Epic>()
            .AsNoTracking()
            .Where(e => e.ProjectId == projectId)
            .OrderBy(e => e.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    public async Task<Story?> GetStoryByIdReadOnlyAsync(StoryId storyId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<Story>()
            .AsNoTracking()
            .Include(s => s.Tasks)
            .FirstOrDefaultAsync(s => s.Id == storyId, cancellationToken);
    }

    public async Task<List<Story>> GetStoriesByEpicIdAsync(EpicId epicId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<Story>()
            .AsNoTracking()
            .Include(s => s.Tasks)
            .Where(s => s.EpicId == epicId)
            .OrderBy(s => s.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    public async Task<WorkTask?> GetTaskByIdReadOnlyAsync(TaskId taskId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<WorkTask>()
            .AsNoTracking()
            .FirstOrDefaultAsync(t => t.Id == taskId, cancellationToken);
    }

    public async Task<List<WorkTask>> GetTasksByStoryIdAsync(StoryId storyId, CancellationToken cancellationToken = default)
    {
        return await _context.Set<WorkTask>()
            .AsNoTracking()
            .Where(t => t.StoryId == storyId)
            .OrderBy(t => t.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    public async Task<Project?> GetProjectByIdReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .AsNoTracking()
            .Include(p => p.Epics)
            .FirstOrDefaultAsync(p => p.Id == projectId, cancellationToken);
    }

    public async Task<List<Project>> GetAllProjectsReadOnlyAsync(CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .AsNoTracking()
            .OrderByDescending(p => p.CreatedAt)
            .ToListAsync(cancellationToken);
    }

    public async Task<Project?> GetProjectWithFullHierarchyReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default)
    {
        return await _context.Projects
            .AsNoTracking()
            .Include(p => p.Epics)
                .ThenInclude(e => e.Stories)
                    .ThenInclude(s => s.Tasks)
            .FirstOrDefaultAsync(p => p.Id == projectId, cancellationToken);
    }
}
