using PlotLine.Data;
using PlotLine.Models;
using PlotLine.ViewModels;

namespace PlotLine.Services;

public interface IFeatureRequestService
{
    Task<FeatureRequest> CreateAsync(int userId, FeatureRequestCreateViewModel model);
    Task<FeatureRequest?> GetForUserAsync(int featureRequestId, int userId);
    Task<FeatureRequest?> GetForAdminAsync(int featureRequestId);
    Task<IReadOnlyList<FeatureRequest>> ListForUserAsync(int userId);
    Task<IReadOnlyList<FeatureRequest>> ListForAdminAsync(AdminFeatureRequestFilter filter);
    Task<IReadOnlyList<FeatureRequestRoadmapSection>> ListRoadmapAsync();
    Task<FeatureRequestAdminSummary> GetAdminSummaryAsync();
    Task<bool> UpdateAdminFieldsAsync(AdminFeatureRequestUpdateViewModel model);
    Task<bool> AddUserReplyAsync(int featureRequestId, int userId, string messageBody);
    Task<bool> AddAdminReplyAsync(int featureRequestId, int adminUserId, string messageBody);
    Task<IReadOnlyList<FeatureRequestMessage>> ListMessagesAsync(int featureRequestId);
    Task<bool> MarkViewedForUserAsync(int featureRequestId, int userId);
    Task<int> CountAttentionForUserAsync(int userId);
    Task<int> CountWaitingForAdminAsync();
}

public sealed class FeatureRequestService(IFeatureRequestRepository featureRequests) : IFeatureRequestService
{
    public Task<FeatureRequest> CreateAsync(int userId, FeatureRequestCreateViewModel model)
    {
        return featureRequests.CreateAsync(
            userId,
            Clean(model.Title),
            Clean(model.Description),
            CleanNullable(model.AppArea),
            CleanNullable(model.Importance));
    }

    public Task<FeatureRequest?> GetForUserAsync(int featureRequestId, int userId) =>
        featureRequests.GetForUserAsync(featureRequestId, userId);

    public Task<FeatureRequest?> GetForAdminAsync(int featureRequestId) =>
        featureRequests.GetForAdminAsync(featureRequestId);

    public Task<IReadOnlyList<FeatureRequest>> ListForUserAsync(int userId) =>
        featureRequests.ListForUserAsync(userId);

    public Task<IReadOnlyList<FeatureRequest>> ListForAdminAsync(AdminFeatureRequestFilter filter)
    {
        var sanitized = SanitizeFilter(filter);
        return featureRequests.ListForAdminAsync(
            sanitized.Status,
            sanitized.AppArea,
            sanitized.Importance,
            sanitized.Search,
            sanitized.Sort);
    }

    public async Task<IReadOnlyList<FeatureRequestRoadmapSection>> ListRoadmapAsync()
    {
        var requests = await featureRequests.ListRoadmapAsync();
        return
        [
            BuildRoadmapSection("Under Consideration", FeatureRequestStatuses.Candidate, requests),
            BuildRoadmapSection("Planned", FeatureRequestStatuses.Planned, requests),
            BuildRoadmapSection("In Progress", FeatureRequestStatuses.InProgress, requests),
            BuildRoadmapSection("Recently Shipped", FeatureRequestStatuses.Shipped, requests
                .OrderByDescending(request => request.ShippedUtc ?? request.UpdatedUtc)
                .ToList())
        ];
    }

    public async Task<FeatureRequestAdminSummary> GetAdminSummaryAsync()
    {
        var requests = await featureRequests.ListForAdminAsync(
            status: null,
            appArea: null,
            importance: null,
            search: null,
            sort: FeatureRequestSortOptions.LatestActivity);

        return new FeatureRequestAdminSummary
        {
            Statuses = FeatureRequestStatuses.All
                .Select(status => new FeatureRequestStatusSummary
                {
                    Status = status,
                    Count = requests.Count(request => string.Equals(request.Status, status, StringComparison.OrdinalIgnoreCase))
                })
                .ToList(),
            TotalOpenRequests = requests.Count(request => request.IsOpen),
            AwaitingAdminReply = requests.Count(request => request.ConversationState == FeatureRequestConversationStates.WaitingForAdminReply),
            AwaitingUserReply = requests.Count(request => request.ConversationState == FeatureRequestConversationStates.WaitingForUserReply)
        };
    }

    public async Task<bool> UpdateAdminFieldsAsync(AdminFeatureRequestUpdateViewModel model)
    {
        if (!FeatureRequestStatuses.IsValid(model.Status))
        {
            return false;
        }

        var current = await featureRequests.GetForAdminAsync(model.FeatureRequestID);
        if (current is null)
        {
            return false;
        }

        var statusChanged = !string.Equals(current.Status, model.Status, StringComparison.OrdinalIgnoreCase);
        var roadmapVisibilityChanged = current.IsPublicCandidate != model.IsPublicCandidate;

        if (statusChanged)
        {
            await featureRequests.UpdateStatusAsync(model.FeatureRequestID, model.Status, model.IsPublicCandidate);
        }
        else if (roadmapVisibilityChanged)
        {
            await featureRequests.UpdateRoadmapVisibilityAsync(model.FeatureRequestID, model.IsPublicCandidate);
        }

        await featureRequests.UpdateAdminNotesAsync(model.FeatureRequestID, CleanNullable(model.AdminNotes));
        return true;
    }

    public async Task<bool> AddUserReplyAsync(int featureRequestId, int userId, string messageBody)
    {
        var message = await featureRequests.AddMessageAsync(featureRequestId, userId, isAdmin: false, Clean(messageBody));
        return message is not null;
    }

    public async Task<bool> AddAdminReplyAsync(int featureRequestId, int adminUserId, string messageBody)
    {
        var message = await featureRequests.AddMessageAsync(featureRequestId, adminUserId, isAdmin: true, Clean(messageBody));
        return message is not null;
    }

    public Task<IReadOnlyList<FeatureRequestMessage>> ListMessagesAsync(int featureRequestId) =>
        featureRequests.ListMessagesAsync(featureRequestId);

    public Task<bool> MarkViewedForUserAsync(int featureRequestId, int userId) =>
        featureRequests.MarkViewedForUserAsync(featureRequestId, userId);

    public Task<int> CountAttentionForUserAsync(int userId) =>
        featureRequests.CountAttentionForUserAsync(userId);

    public Task<int> CountWaitingForAdminAsync() =>
        featureRequests.CountWaitingForAdminAsync();

    private static string Clean(string value) => value.Trim();

    private static string? CleanNullable(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim();

    private static AdminFeatureRequestFilter SanitizeFilter(AdminFeatureRequestFilter filter)
    {
        return new AdminFeatureRequestFilter
        {
            Status = FeatureRequestStatuses.IsValid(filter.Status) ? filter.Status : null,
            AppArea = FeatureRequestOptions.AppAreas.Contains(filter.AppArea ?? string.Empty, StringComparer.OrdinalIgnoreCase) ? filter.AppArea : null,
            Importance = FeatureRequestOptions.ImportanceOptions.Contains(filter.Importance ?? string.Empty, StringComparer.OrdinalIgnoreCase) ? filter.Importance : null,
            Search = CleanNullable(filter.Search),
            Sort = FeatureRequestSortOptions.IsValid(filter.Sort) ? filter.Sort : FeatureRequestSortOptions.LatestActivity
        };
    }

    private static FeatureRequestRoadmapSection BuildRoadmapSection(string heading, string status, IEnumerable<FeatureRequest> requests)
    {
        return new FeatureRequestRoadmapSection
        {
            Heading = heading,
            Status = status,
            Requests = requests
                .Where(request => string.Equals(request.Status, status, StringComparison.OrdinalIgnoreCase))
                .ToList()
        };
    }
}
