package comment

import (
	"context"
	"errors"
	"fmt"
	"tercul/internal/app/analytics"
	"tercul/internal/app/authz"
	"tercul/internal/domain"
	platform_auth "tercul/internal/platform/auth"
	"tercul/internal/platform/log"
)

// CommentCommands contains the command handlers for the comment aggregate.
type CommentCommands struct {
	repo         domain.CommentRepository
	authzSvc     *authz.Service
	analyticsSvc analytics.Service
}

// NewCommentCommands creates a new CommentCommands handler.
func NewCommentCommands(repo domain.CommentRepository, authzSvc *authz.Service, analyticsSvc analytics.Service) *CommentCommands {
	return &CommentCommands{
		repo:         repo,
		authzSvc:     authzSvc,
		analyticsSvc: analyticsSvc,
	}
}

// CreateCommentInput represents the input for creating a new comment.
type CreateCommentInput struct {
	Text          string
	UserID        uint
	WorkID        *uint
	TranslationID *uint
	ParentID      *uint
}

// CreateComment creates a new comment.
func (c *CommentCommands) CreateComment(ctx context.Context, input CreateCommentInput) (*domain.Comment, error) {
	comment := &domain.Comment{
		Text:          input.Text,
		UserID:        input.UserID,
		WorkID:        input.WorkID,
		TranslationID: input.TranslationID,
		ParentID:      input.ParentID,
	}
	err := c.repo.Create(ctx, comment)
	if err != nil {
		return nil, err
	}

	if c.analyticsSvc != nil {
		if input.WorkID != nil {
			go func() {
				if err := c.analyticsSvc.IncrementWorkComments(context.Background(), *input.WorkID); err != nil {
					log.Error(err, "failed to increment work comments")
				}
			}()
		}
		if input.TranslationID != nil {
			go func() {
				if err := c.analyticsSvc.IncrementTranslationComments(context.Background(), *input.TranslationID); err != nil {
					log.Error(err, "failed to increment translation comments")
				}
			}()
		}
	}

	return comment, nil
}

// UpdateCommentInput represents the input for updating an existing comment.
type UpdateCommentInput struct {
	ID   uint
	Text string
}

// UpdateComment updates an existing comment after an authorization check.
func (c *CommentCommands) UpdateComment(ctx context.Context, input UpdateCommentInput) (*domain.Comment, error) {
	userID, ok := platform_auth.GetUserIDFromContext(ctx)
	if !ok {
		return nil, domain.ErrUnauthorized
	}

	comment, err := c.repo.GetByID(ctx, input.ID)
	if err != nil {
		if errors.Is(err, domain.ErrEntityNotFound) {
			return nil, fmt.Errorf("%w: comment with id %d not found", domain.ErrEntityNotFound, input.ID)
		}
		return nil, err
	}

	can, err := c.authzSvc.CanDeleteComment(ctx, userID, comment) // Using CanDeleteComment for editing as well
	if err != nil {
		return nil, err
	}
	if !can {
		return nil, domain.ErrForbidden
	}

	comment.Text = input.Text
	err = c.repo.Update(ctx, comment)
	if err != nil {
		return nil, err
	}
	return comment, nil
}

// DeleteComment deletes a comment by ID after an authorization check.
func (c *CommentCommands) DeleteComment(ctx context.Context, id uint) error {
	userID, ok := platform_auth.GetUserIDFromContext(ctx)
	if !ok {
		return domain.ErrUnauthorized
	}

	comment, err := c.repo.GetByID(ctx, id)
	if err != nil {
		if errors.Is(err, domain.ErrEntityNotFound) {
			return fmt.Errorf("%w: comment with id %d not found", domain.ErrEntityNotFound, id)
		}
		return err
	}

	can, err := c.authzSvc.CanDeleteComment(ctx, userID, comment)
	if err != nil {
		return err
	}
	if !can {
		return domain.ErrForbidden
	}

	return c.repo.Delete(ctx, id)
}