using Application.Abstractions.Messaging;
using Application.Abstractions.Data;
using Domain.Entities.Forum.Logs;
using Domain.Entities.Forum.ValueObject;
using SharedKernel.Results;
using Microsoft.EntityFrameworkCore;

namespace Application.Features.Api.Forum.Post.Get;

public sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Result<Response>>
{
    public async Task<Result<Response>> Handle(Query request, CancellationToken ct)
    {
        // 게시글 조회
        var item = await db.Post
            .Include(c => c.Board)
            .Include(c => c.BoardPrefix)
            .Include(c => c.Member).ThenInclude(m => m.MemberGrade)
            .Include(c => c.PostTag).ThenInclude(t => t.Tag)
            .FirstOrDefaultAsync(x => x.ID == request.ID && !x.IsDeleted, ct);

        if (item is null)
        {
            return Result.Failure<Response>(
                Error.NotFound("Post.NotFound", "게시글을 찾을 수 없습니다.")
            );
        }

        // 조회수 증가 (24시간 내 중복 방지)
        var cutoff = DateTime.UtcNow.AddHours(-24);
        bool isDuplicate;

        if (request.MemberID is int viewerID)
        {
            isDuplicate = await db.PostViewLog.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == viewerID && x.CreatedAt > cutoff, ct);
        }
        else
        {
            isDuplicate = await db.PostViewLog.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == null && x.IpAddress == request.IpAddress && x.CreatedAt > cutoff, ct);
        }

        if (!isDuplicate)
        {
            item.Views++;
            db.PostViewLog.Add(new PostViewLog
            {
                PostID = item.ID,
                MemberID = request.MemberID,
                IpAddress = request.IpAddress,
            });
            await db.SaveChangesAsync(ct);
        }

        // 이전/다음 게시글
        var prevID = await db.Post.AsNoTracking()
            .Where(x => x.BoardID == item.BoardID && x.ID < item.ID && !x.IsDeleted)
            .OrderByDescending(x => x.ID)
            .Select(x => (int?)x.ID)
            .FirstOrDefaultAsync(ct);

        var nextID = await db.Post.AsNoTracking()
            .Where(x => x.BoardID == item.BoardID && x.ID > item.ID && !x.IsDeleted)
            .OrderBy(x => x.ID)
            .Select(x => (int?)x.ID)
            .FirstOrDefaultAsync(ct);

        // 사용자별 상태
        bool hasLike = false, hasDislike = false, hasBookmark = false, hasReport = false;

        if (request.MemberID is int memberID)
        {
            hasLike = await db.PostReaction.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == memberID && x.Reaction == Reaction.Like, ct);
            hasDislike = await db.PostReaction.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == memberID && x.Reaction == Reaction.Dislike, ct);
            hasBookmark = await db.PostBookmark.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == memberID, ct);
            hasReport = await db.PostReport.AsNoTracking().AnyAsync(x => x.PostID == item.ID && x.MemberID == memberID, ct);
        }

        // 태그 목록
        var tagList = item.PostTag.Select(t => new Response.TagDto(t.Tag.ID, t.Tag.Slug)).ToList();

        // 말머리
        Response.BoardPrefixDto? boardPrefix = item.BoardPrefix is not null
            ? new Response.BoardPrefixDto(
                item.BoardPrefix.ID,
                item.BoardPrefix.BoardID,
                item.BoardPrefix.Name,
                item.BoardPrefix.Color,
                item.BoardPrefix.Posts
            ) : null;

        // 작성자
        Response.WriterDto? writer = item.Member is not null
            ? new Response.WriterDto(
                item.Member.ID,
                item.Member.SID,
                item.Member.Name,
                item.Member.Thumb,
                item.Member.Icon,
                item.Member.MemberGrade?.Image,
                item.Member.Summary,
                item.Member.CreatedAt
            ) : null;

        return new Response(
            item.ID,
            item.BoardID,
            item.Board.Code,
            item.BoardPrefixID,
            item.MemberID,
            item.Subject,
            item.Content,
            item.SID,
            item.Email,
            item.Name,
            boardPrefix,
            writer,
            item.IsReply,
            item.IsAnonymous,
            item.IsSecret,
            item.IsNotice,
            item.IsSpeaker,
            item.Views,
            item.Likes,
            item.Dislikes,
            item.Comments,
            item.Reports,
            item.Images,
            item.Medias,
            item.Files,
            item.Tags,
            item.IpAddress,
            item.CreatedAt,
            tagList,
            prevID,
            nextID,
            hasLike,
            hasDislike,
            hasBookmark,
            hasReport
        );
    }
}