using System.Collections.Generic;
using System.Linq;
using CMSMicroservice.Protobuf.Protos.BlogPost;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.BlogPostCQ.Commands.CreateBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Commands.UpdateBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Commands.DeleteBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Commands.PublishBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Commands.ArchiveBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Commands.IncrementViewCount;
using CMSMicroservice.Application.BlogPostCQ.Queries.GetBlogPost;
using CMSMicroservice.Application.BlogPostCQ.Queries.GetBlogPostBySlug;
using CMSMicroservice.Application.BlogPostCQ.Queries.GetAllBlogPosts;
using CMSMicroservice.Application.BlogPostCQ.Queries.GetPublishedBlogPosts;
using CMSMicroservice.Application.BlogPostCQ.Queries.GetFeaturedBlogPosts;
using CMSMicroservice.Domain.Enums;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Mapster;
using MediatR;
using AppModels = CMSMicroservice.Application.Common.Models;
using ProtoMetaData = CMSMicroservice.Protobuf.Protos.MetaData;

namespace CMSMicroservice.WebApi.Services;

public class BlogPostService : BlogPostContract.BlogPostContractBase
{
    private readonly ISender _sender;
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;

    public BlogPostService(ISender sender, IDispatchRequestToCQRS dispatchRequestToCQRS)
    {
        _sender = sender;
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
    }

    public override async Task<CreateBlogPostResponse> CreateBlogPost(CreateBlogPostRequest request, ServerCallContext context)
    {
        var command = new CreateBlogPostCommand
        {
            Title = request.Title,
            Slug = request.Slug,
            Summary = request.Summary,
            HtmlContent = request.HtmlContent,
            FeaturedImagePath = request.FeaturedImagePath,
            FeaturedImageThumbnailPath = request.FeaturedImageThumbnailPath,
            CategoryIds = request.CategoryIds.ToList(),
            TagIds = request.TagIds.ToList(),
            IsFeatured = request.IsFeatured,
            SortOrder = request.SortOrder,
            ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
            ImageFileMime = request.ImageFile?.Mime,
            ImageFileName = request.ImageFile?.FileName
        };

        var result = await _sender.Send(command, context.CancellationToken);
        return new CreateBlogPostResponse { Id = result };
    }

    public override async Task<Empty> UpdateBlogPost(UpdateBlogPostRequest request, ServerCallContext context)
    {
        var command = new UpdateBlogPostCommand
        {
            Id = request.Id,
            Title = request.Title,
            Slug = request.Slug,
            Summary = request.Summary,
            HtmlContent = request.HtmlContent,
            FeaturedImagePath = request.FeaturedImagePath,
            FeaturedImageThumbnailPath = request.FeaturedImageThumbnailPath,
            CategoryIds = request.CategoryIds.ToList(),
            TagIds = request.TagIds.ToList(),
            IsFeatured = request.IsFeatured,
            SortOrder = request.SortOrder,
            ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
            ImageFileMime = request.ImageFile?.Mime,
            ImageFileName = request.ImageFile?.FileName
        };

        await _sender.Send(command, context.CancellationToken);
        return new Empty();
    }

    public override async Task<Empty> DeleteBlogPost(DeleteBlogPostRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeleteBlogPostRequest, DeleteBlogPostCommand>(request, context);
    }

    public override async Task<GetBlogPostResponse> GetBlogPost(GetBlogPostRequest request, ServerCallContext context)
    {
        var query = new GetBlogPostQuery { Id = request.Id };
        var result = await _sender.Send(query, context.CancellationToken);
        return MapBlogPostDtoToResponse(result);
    }

    public override async Task<GetBlogPostResponse> GetBlogPostBySlug(GetBlogPostBySlugRequest request, ServerCallContext context)
    {
        var query = new GetBlogPostBySlugQuery { Slug = request.Slug };
        var result = await _sender.Send(query, context.CancellationToken);
        return MapBlogPostDtoToResponse(result);
    }

    public override async Task<GetAllBlogPostsResponse> GetAllBlogPosts(GetAllBlogPostsRequest request, ServerCallContext context)
    {
        var query = new GetAllBlogPostsQuery
        {
            PageNumber = request.PaginationState?.PageNumber ?? 1,
            PageSize = request.PaginationState?.PageSize ?? 10,
            SortBy = request.SortBy,
            SearchTerm = request.Filter?.SearchTerm,
            Status = request.Filter?.Status.HasValue == true ? (BlogPostStatus?)request.Filter.Status.Value : null,
            CategoryId = request.Filter?.CategoryId,
            IsFeatured = request.Filter?.IsFeatured
        };

        var result = await _sender.Send(query, context.CancellationToken);
        return MapAllBlogPostsResponse(result);
    }

    public override async Task<GetAllBlogPostsResponse> GetPublishedBlogPosts(GetPublishedBlogPostsRequest request, ServerCallContext context)
    {
        var query = new GetPublishedBlogPostsQuery
        {
            PageNumber = request.PaginationState?.PageNumber ?? 1,
            PageSize = request.PaginationState?.PageSize ?? 10,
            SearchTerm = request.SearchTerm,
            CategoryId = request.CategoryId
        };

        var result = await _sender.Send(query, context.CancellationToken);
        return MapAllBlogPostsResponse(result);
    }

    public override async Task<GetAllBlogPostsResponse> GetFeaturedBlogPosts(GetFeaturedBlogPostsRequest request, ServerCallContext context)
    {
        var query = new GetFeaturedBlogPostsQuery { Count = request.Count > 0 ? request.Count : 5 };
        var items = await _sender.Send(query, context.CancellationToken);

        var response = new GetAllBlogPostsResponse();
        foreach (var item in items)
            response.Models.Add(MapToListItem(item));
        return response;
    }

    public override async Task<PublishBlogPostResponse> PublishBlogPost(PublishBlogPostRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<PublishBlogPostRequest, PublishBlogPostCommand, PublishBlogPostResponse>(request, context);
    }

    public override async Task<ArchiveBlogPostResponse> ArchiveBlogPost(ArchiveBlogPostRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<ArchiveBlogPostRequest, ArchiveBlogPostCommand, ArchiveBlogPostResponse>(request, context);
    }

    public override async Task<Empty> IncrementViewCount(IncrementViewCountRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<IncrementViewCountRequest, IncrementViewCountCommand>(request, context);
    }

    // ── Private Mapping Helpers ──

    private static GetBlogPostResponse MapBlogPostDtoToResponse(BlogPostDto dto)
    {
        var response = new GetBlogPostResponse
        {
            Id = dto.Id,
            Title = dto.Title ?? string.Empty,
            Slug = dto.Slug ?? string.Empty,
            Summary = dto.Summary,
            HtmlContent = dto.HtmlContent ?? string.Empty,
            FeaturedImagePath = dto.FeaturedImagePath,
            FeaturedImageThumbnailPath = dto.FeaturedImageThumbnailPath,
            Status = (int)dto.Status,
            StatusName = dto.StatusName ?? string.Empty,
            ViewCount = dto.ViewCount,
            AuthorUserId = dto.AuthorUserId,
            IsFeatured = dto.IsFeatured,
            SortOrder = dto.SortOrder,
            Created = dto.Created != default ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.Created, DateTimeKind.Utc)) : null,
            LastModified = dto.LastModified.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.LastModified.Value, DateTimeKind.Utc)) : null,
            PublishedAt = dto.PublishedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(dto.PublishedAt.Value, DateTimeKind.Utc)) : null
        };

        if (dto.Categories != null)
            foreach (var c in dto.Categories)
                response.Categories.Add(new BlogPostCategoryInfo { Id = c.Id, Title = c.Title ?? string.Empty, Slug = c.Slug ?? string.Empty });

        if (dto.Tags != null)
            foreach (var t in dto.Tags)
                response.Tags.Add(new BlogPostTagInfo { Id = t.Id, Title = t.Title ?? string.Empty, Name = t.Name ?? string.Empty });

        return response;
    }

    private static GetAllBlogPostsResponse MapAllBlogPostsResponse(GetAllBlogPostsResponseDto dto)
    {
        var response = new GetAllBlogPostsResponse
        {
            MetaData = dto.MetaData.Adapt<ProtoMetaData>()
        };

        foreach (var item in dto.Models)
            response.Models.Add(MapToListItem(item));

        return response;
    }

    private static BlogPostListItem MapToListItem(BlogPostListItemDto item)
    {
        var listItem = new BlogPostListItem
        {
            Id = item.Id,
            Title = item.Title ?? string.Empty,
            Slug = item.Slug ?? string.Empty,
            Summary = item.Summary,
            FeaturedImageThumbnailPath = item.FeaturedImageThumbnailPath,
            Status = item.Status,
            StatusName = item.StatusName ?? string.Empty,
            ViewCount = item.ViewCount,
            IsFeatured = item.IsFeatured,
            Created = item.Created != default ? Timestamp.FromDateTime(DateTime.SpecifyKind(item.Created, DateTimeKind.Utc)) : null,
            PublishedAt = item.PublishedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(item.PublishedAt.Value, DateTimeKind.Utc)) : null
        };

        if (item.Categories != null)
            foreach (var c in item.Categories)
                listItem.Categories.Add(new BlogPostCategoryInfo { Id = c.Id, Title = c.Title ?? string.Empty, Slug = c.Slug ?? string.Empty });

        return listItem;
    }
}
