using Microsoft.EntityFrameworkCore;
using WorkClub.Application.Interfaces;
using WorkClub.Application.Tasks.DTOs;
using WorkClub.Domain.Entities;
using WorkClub.Domain.Enums;
using WorkClub.Infrastructure.Data;

namespace WorkClub.Api.Services;

public class TaskService
{
    private readonly AppDbContext _context;
    private readonly ITenantProvider _tenantProvider;

    public TaskService(AppDbContext context, ITenantProvider tenantProvider)
    {
        _context = context;
        _tenantProvider = tenantProvider;
    }

    public async Task<TaskListDto> GetTasksAsync(string? statusFilter, int page, int pageSize, string? currentExternalUserId = null)
    {
        var query = _context.WorkItems.AsQueryable();

        if (!string.IsNullOrEmpty(statusFilter))
        {
            if (Enum.TryParse<WorkItemStatus>(statusFilter, ignoreCase: true, out var status))
            {
                query = query.Where(w => w.Status == status);
            }
        }

  var total = await query.CountAsync();

  var items = await query
    .OrderBy(w => w.CreatedAt)
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();

  // Get current member ID for IsAssignedToMe check
  Guid? currentMemberId = null;
  if (currentExternalUserId != null)
  {
    var tenantId = _tenantProvider.GetTenantId();
    currentMemberId = await _context.Members
      .Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
      .Select(m => m.Id)
      .FirstOrDefaultAsync();
  }

  // Get all assignee IDs to fetch their names in bulk
  var assigneeIds = items.Where(w => w.AssigneeId.HasValue).Select(w => w.AssigneeId!.Value).Distinct().ToList();
  var assigneeNames = await _context.Members
    .Where(m => assigneeIds.Contains(m.Id))
    .Select(m => new { m.Id, m.DisplayName })
    .ToDictionaryAsync(m => m.Id, m => m.DisplayName);

  var itemDtos = items.Select(w => new TaskListItemDto(
    w.Id,
    w.Title,
    w.Status.ToString(),
    w.AssigneeId,
    w.AssigneeId.HasValue && assigneeNames.TryGetValue(w.AssigneeId.Value, out var name) ? name : null,
    w.CreatedAt,
    currentMemberId != null && w.AssigneeId == currentMemberId
  )).ToList();

  return new TaskListDto(itemDtos, total, page, pageSize);
}

public async Task<TaskDetailDto?> GetTaskByIdAsync(Guid id, string? currentExternalUserId = null)
{
  var workItem = await _context.WorkItems.FindAsync(id);

  if (workItem == null)
    return null;

  // Get current member ID for IsAssignedToMe check
  Guid? currentMemberId = null;
  if (currentExternalUserId != null)
  {
    var tenantId = _tenantProvider.GetTenantId();
    currentMemberId = await _context.Members
      .Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
      .Select(m => m.Id)
      .FirstOrDefaultAsync();
  }

  // Fetch assignee and creator names
  var memberIds = new List<Guid>();
  if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);
  memberIds.Add(workItem.CreatedById);

  var memberNames = await _context.Members
    .Where(m => memberIds.Contains(m.Id))
    .Select(m => new { m.Id, m.DisplayName })
    .ToDictionaryAsync(m => m.Id, m => m.DisplayName);

  return new TaskDetailDto(
    workItem.Id,
    workItem.Title,
    workItem.Description,
    workItem.Status.ToString(),
    workItem.AssigneeId,
    workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
    workItem.CreatedById,
    memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
    workItem.ClubId,
    workItem.DueDate,
    workItem.CreatedAt,
    workItem.UpdatedAt,
    currentMemberId != null && workItem.AssigneeId == currentMemberId
  );
}

    public async Task<(TaskDetailDto? task, string? error)> CreateTaskAsync(CreateTaskRequest request, Guid createdById)
    {
        var tenantId = _tenantProvider.GetTenantId();

        var workItem = new WorkItem
        {
            Id = Guid.NewGuid(),
            TenantId = tenantId,
            Title = request.Title,
            Description = request.Description,
            Status = WorkItemStatus.Open,
            ClubId = request.ClubId,
            AssigneeId = request.AssigneeId,
            DueDate = request.DueDate,
            CreatedById = createdById,
            CreatedAt = DateTimeOffset.UtcNow,
            UpdatedAt = DateTimeOffset.UtcNow
        };

  _context.WorkItems.Add(workItem);
  await _context.SaveChangesAsync();

  // Fetch creator and assignee names
  var memberIds = new List<Guid> { createdById };
  if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);

  var memberNames = await _context.Members
    .Where(m => memberIds.Contains(m.Id))
    .Select(m => new { m.Id, m.DisplayName })
    .ToDictionaryAsync(m => m.Id, m => m.DisplayName);

  var dto = new TaskDetailDto(
    workItem.Id,
    workItem.Title,
    workItem.Description,
    workItem.Status.ToString(),
    workItem.AssigneeId,
    workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
    workItem.CreatedById,
    memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
    workItem.ClubId,
    workItem.DueDate,
    workItem.CreatedAt,
    workItem.UpdatedAt,
    false
  );

  return (dto, null);
    }

    public async Task<(TaskDetailDto? task, string? error, bool isConflict)> UpdateTaskAsync(Guid id, UpdateTaskRequest request, string? currentExternalUserId = null)
    {
        var workItem = await _context.WorkItems.FindAsync(id);

        if (workItem == null)
            return (null, "Task not found", false);

        if (request.Title != null)
            workItem.Title = request.Title;

        if (request.Description != null)
            workItem.Description = request.Description;

        if (request.AssigneeId.HasValue)
            workItem.AssigneeId = request.AssigneeId;

        if (request.DueDate.HasValue)
            workItem.DueDate = request.DueDate;

        if (request.Status != null)
        {
            if (!Enum.TryParse<WorkItemStatus>(request.Status, ignoreCase: true, out var newStatus))
            {
                return (null, $"Invalid status: {request.Status}", false);
            }

            if (!workItem.CanTransitionTo(newStatus))
            {
                return (null, $"Cannot transition from {workItem.Status} to {newStatus}", false);
            }

            workItem.TransitionTo(newStatus);
        }

        workItem.UpdatedAt = DateTimeOffset.UtcNow;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            return (null, "Task was modified by another user. Please refresh and try again.", true);
        }

  // Get current member ID for IsAssignedToMe check
  Guid? currentMemberId = null;
  if (currentExternalUserId != null)
  {
    var tenantId = _tenantProvider.GetTenantId();
    currentMemberId = await _context.Members
      .Where(m => m.ExternalUserId == currentExternalUserId && m.TenantId == tenantId)
      .Select(m => m.Id)
      .FirstOrDefaultAsync();
  }

  // Fetch assignee and creator names
  var memberIds = new List<Guid>();
  if (workItem.AssigneeId.HasValue) memberIds.Add(workItem.AssigneeId.Value);
  memberIds.Add(workItem.CreatedById);

  var memberNames = await _context.Members
    .Where(m => memberIds.Contains(m.Id))
    .Select(m => new { m.Id, m.DisplayName })
    .ToDictionaryAsync(m => m.Id, m => m.DisplayName);

  var dto = new TaskDetailDto(
    workItem.Id,
    workItem.Title,
    workItem.Description,
    workItem.Status.ToString(),
    workItem.AssigneeId,
    workItem.AssigneeId.HasValue && memberNames.TryGetValue(workItem.AssigneeId.Value, out var assigneeName) ? assigneeName : null,
    workItem.CreatedById,
    memberNames.TryGetValue(workItem.CreatedById, out var createdByName) ? createdByName : null,
    workItem.ClubId,
    workItem.DueDate,
    workItem.CreatedAt,
    workItem.UpdatedAt,
    currentMemberId != null && workItem.AssigneeId == currentMemberId
  );

  return (dto, null, false);
}

    public async Task<bool> DeleteTaskAsync(Guid id)
    {
        var workItem = await _context.WorkItems.FindAsync(id);

        if (workItem == null)
            return false;

        _context.WorkItems.Remove(workItem);
        await _context.SaveChangesAsync();

        return true;
    }

    public async Task<(bool success, string? error)> AssignToMeAsync(Guid taskId, string externalUserId)
    {
        var tenantId = _tenantProvider.GetTenantId();
        var memberId = await _context.Members
            .Where(m => m.ExternalUserId == externalUserId && m.TenantId == tenantId)
            .Select(m => m.Id)
            .FirstOrDefaultAsync();

        if (memberId == Guid.Empty)
            return (false, "User is not a member of this club");

        var workItem = await _context.WorkItems.FindAsync(taskId);
        if (workItem == null)
            return (false, "Task not found");

        if (workItem.AssigneeId.HasValue)
            return (false, "Task is already assigned");

        workItem.AssigneeId = memberId;
        
        if (workItem.CanTransitionTo(WorkItemStatus.Assigned))
            workItem.TransitionTo(WorkItemStatus.Assigned);

        workItem.UpdatedAt = DateTimeOffset.UtcNow;
        
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            return (false, "Task was modified by another user");
        }

        return (true, null);
    }

    public async Task<(bool success, string? error)> UnassignFromMeAsync(Guid taskId, string externalUserId)
    {
        var tenantId = _tenantProvider.GetTenantId();
        var memberId = await _context.Members
            .Where(m => m.ExternalUserId == externalUserId && m.TenantId == tenantId)
            .Select(m => m.Id)
            .FirstOrDefaultAsync();

        if (memberId == Guid.Empty)
            return (false, "User is not a member of this club");

        var workItem = await _context.WorkItems.FindAsync(taskId);
        if (workItem == null)
            return (false, "Task not found");

        if (workItem.AssigneeId != memberId)
            return (false, "Task is not assigned to you");

        workItem.AssigneeId = null;

        if (workItem.Status == WorkItemStatus.Assigned || workItem.Status == WorkItemStatus.InProgress)
        {
            // Transition back to open if no longer assigned and not marked Review/Done
            workItem.Status = WorkItemStatus.Open;
        }

        workItem.UpdatedAt = DateTimeOffset.UtcNow;
        
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            return (false, "Task was modified by another user");
        }

        return (true, null);
    }
}
