package service

import (
	"api_bb/internal/models"
	"api_bb/internal/repository"
	"api_bb/pkg/logger"
	"errors"

	"go.uber.org/zap"
)

type NewsService interface {
	CreateNews(req models.CreateNewsRequest, authorID uint) (*models.NewsResponse, error)
	GetNewsByID(id uint) (*models.NewsResponse, error)
	GetAllNews(limit, offset int, category string) ([]models.NewsResponse, int64, error)
	UpdateNews(id uint, req models.UpdateNewsRequest, userID uint) (*models.NewsResponse, error)
	DeleteNews(id uint, userID uint) error
	IncrementViews(id uint) error
	CreateComment(newsID uint, req models.CreateCommentRequest, authorID uint) (*models.CommentResponse, error)
	GetCommentsByNewsID(newsID uint) ([]models.CommentResponse, error)
	DeleteComment(commentID, userID uint) error
	GetUserNews(userID uint, limit, offset int) ([]models.NewsResponse, int64, error)
}

type newsService struct {
	newsRepo    repository.NewsRepository
	commentRepo repository.CommentRepository
	logger      logger.LoggerInterface
}

func NewNewsService(newsRepo repository.NewsRepository, commentRepo repository.CommentRepository, log logger.LoggerInterface) NewsService {

	serviceLogger := log.With(zap.String("service", "news"))

	return &newsService{
		newsRepo:    newsRepo,
		commentRepo: commentRepo,
		logger:      serviceLogger,
	}
}

func (s *newsService) CreateNews(req models.CreateNewsRequest, authorID uint) (*models.NewsResponse, error) {
	news := &models.News{
		Title:    req.Title,
		Excerpt:  req.Excerpt,
		Content:  req.Content,
		Image:    req.Image,
		Category: req.Category,
		AuthorID: authorID,
	}

	if err := s.newsRepo.Create(news); err != nil {
		s.logger.Error("Failed to create news", zap.Error(err))
		return nil, errors.New("failed to create news")
	}

	// Получаем созданную новость с автором
	createdNews, err := s.newsRepo.GetByID(news.ID)
	if err != nil {
		return nil, err
	}

	return s.toNewsResponse(createdNews), nil
}

func (s *newsService) GetNewsByID(id uint) (*models.NewsResponse, error) {
	news, err := s.newsRepo.GetByID(id)
	if err != nil {
		return nil, errors.New("news not found")
	}

	// Увеличиваем счетчик просмотров
	go s.newsRepo.IncrementViews(id)

	return s.toNewsResponse(news), nil
}

func (s *newsService) GetAllNews(limit, offset int, category string) ([]models.NewsResponse, int64, error) {
	news, total, err := s.newsRepo.GetAll(limit, offset, category)
	if err != nil {
		return nil, 0, err
	}

	responses := make([]models.NewsResponse, len(news))
	for i, n := range news {
		responses[i] = *s.toNewsResponse(&n)
	}

	return responses, total, nil
}

func (s *newsService) UpdateNews(id uint, req models.UpdateNewsRequest, userID uint) (*models.NewsResponse, error) {
	news, err := s.newsRepo.GetByID(id)
	if err != nil {
		return nil, errors.New("news not found")
	}

	// Проверяем права доступа
	if news.AuthorID != userID {
		return nil, errors.New("access denied")
	}

	// Обновляем поля
	if req.Title != "" {
		news.Title = req.Title
	}
	if req.Excerpt != "" {
		news.Excerpt = req.Excerpt
	}
	if req.Content != "" {
		news.Content = req.Content
	}
	if req.Image != "" {
		news.Image = req.Image
	}
	if req.Category != "" {
		news.Category = req.Category
	}

	if err := s.newsRepo.Update(news); err != nil {
		return nil, err
	}

	return s.toNewsResponse(news), nil
}

func (s *newsService) DeleteNews(id uint, userID uint) error {
	news, err := s.newsRepo.GetByID(id)
	if err != nil {
		return errors.New("news not found")
	}

	// Проверяем права доступа
	if news.AuthorID != userID {
		return errors.New("access denied")
	}

	return s.newsRepo.Delete(id)
}

func (s *newsService) IncrementViews(id uint) error {
	return s.newsRepo.IncrementViews(id)
}

func (s *newsService) CreateComment(newsID uint, req models.CreateCommentRequest, authorID uint) (*models.CommentResponse, error) {
	// Проверяем существование новости
	_, err := s.newsRepo.GetByID(newsID)
	if err != nil {
		return nil, errors.New("news not found")
	}

	comment := &models.Comment{
		Content:  req.Content,
		NewsID:   newsID,
		AuthorID: authorID,
	}

	if err := s.commentRepo.Create(comment); err != nil {
		return nil, err
	}

	// Получаем созданный комментарий с автором
	createdComment, err := s.commentRepo.GetByID(comment.ID)
	if err != nil {
		return nil, err
	}

	return s.toCommentResponse(createdComment), nil
}

func (s *newsService) GetCommentsByNewsID(newsID uint) ([]models.CommentResponse, error) {
	comments, err := s.commentRepo.GetByNewsID(newsID)
	if err != nil {
		return nil, err
	}

	responses := make([]models.CommentResponse, len(comments))
	for i, c := range comments {
		responses[i] = *s.toCommentResponse(&c)
	}

	return responses, nil
}

func (s *newsService) DeleteComment(commentID, userID uint) error {
	comment, err := s.commentRepo.GetByID(commentID)
	if err != nil {
		return errors.New("comment not found")
	}

	// Проверяем права доступа
	if comment.AuthorID != userID {
		return errors.New("access denied")
	}

	return s.commentRepo.Delete(commentID)
}

func (s *newsService) GetUserNews(userID uint, limit, offset int) ([]models.NewsResponse, int64, error) {
	news, total, err := s.newsRepo.GetByAuthor(userID, limit, offset)
	if err != nil {
		return nil, 0, err
	}

	responses := make([]models.NewsResponse, len(news))
	for i, n := range news {
		responses[i] = *s.toNewsResponse(&n)
	}

	return responses, total, nil
}

// Вспомогательные методы для преобразования
func (s *newsService) toNewsResponse(news *models.News) *models.NewsResponse {
	return &models.NewsResponse{
		ID:        news.ID,
		CreatedAt: news.CreatedAt,
		UpdatedAt: news.UpdatedAt,
		Title:     news.Title,
		Excerpt:   news.Excerpt,
		Content:   news.Content,
		Image:     news.Image,
		Category:  news.Category,
		Views:     news.Views,
		Author: models.AuthorInfo{
			ID:        news.Author.ID,
			FirstName: news.Author.FirstName,
			LastName:  news.Author.LastName,
		},
		Comments: len(news.Comments),
	}
}

func (s *newsService) toCommentResponse(comment *models.Comment) *models.CommentResponse {
	return &models.CommentResponse{
		ID:        comment.ID,
		CreatedAt: comment.CreatedAt,
		Content:   comment.Content,
		Author: models.AuthorInfo{
			ID:        comment.Author.ID,
			FirstName: comment.Author.FirstName,
			LastName:  comment.Author.LastName,
		},
	}
}
