package object

import (
	"api_yal/internal/domain/account"
	"api_yal/internal/models"
	"api_yal/internal/repository"
	"context"
	"errors"
	"fmt"

	"gorm.io/gorm"
)

type ObjectService interface {
	GetObjectByID(ctx context.Context, id uint) (*ObjectResponse, error)
	CreateObject(ctx context.Context, req *CreateObjectRequest) (*ObjectResponse, error)
	UpdateObject(ctx context.Context, id uint, req *UpdateObjectRequest) (*ObjectResponse, error)
	DeleteObject(ctx context.Context, id uint) error
	ListObjects(ctx context.Context, req *ListObjectsRequest) (*ObjectListResponse, error)
	GetObjectsByOwner(ctx context.Context, ownerID uint, page, pageSize int) (*ObjectListResponse, error)
	GetObjectsByType(ctx context.Context, objectType string, page, pageSize int) (*ObjectListResponse, error)
	SearchObjects(ctx context.Context, query string, page, pageSize int) (*ObjectListResponse, error)
	GetNearbyObjects(ctx context.Context, lat, lng, radius float64, page, pageSize int) (*ObjectListResponse, error)
	ToggleVerification(ctx context.Context, id uint, verified bool) error

	// Feedback methods
	CreateFeedback(ctx context.Context, req *CreateFeedbackRequest, ownerID uint) (*FeedbackResponse, error)
	UpdateFeedback(ctx context.Context, id uint, req *UpdateFeedbackRequest, ownerID uint) (*FeedbackResponse, error)
	DeleteFeedback(ctx context.Context, id uint, ownerID uint) error
	GetFeedbackByID(ctx context.Context, id uint) (*FeedbackResponse, error)
	GetFeedbacksByObject(ctx context.Context, objectID uint, page, pageSize int) (*FeedbackListResponse, error)

	// Rating methods
	CreateRatingVote(ctx context.Context, req *CreateRatingVoteRequest, voterID uint) (*RatingVoteResponse, error)
	GetObjectRating(ctx context.Context, objectID uint, platform models.PlatformType) (*RatingResponse, error)
	GetUserRatingVote(ctx context.Context, objectID uint, userID uint, platform models.PlatformType) (*RatingVoteResponse, error)
}

type objectServiceImpl struct {
	objectRepository repository.ObjectRepository
}

func NewObjectService(objectRepository repository.ObjectRepository) ObjectService {
	return &objectServiceImpl{
		objectRepository: objectRepository,
	}
}

// GetObjectByID возвращает объект по ID с полной информацией
func (s *objectServiceImpl) GetObjectByID(ctx context.Context, id uint) (*ObjectResponse, error) {
	object, err := s.objectRepository.GetByID(id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrObjectNotFound
		}
		return nil, fmt.Errorf("failed to get object by id: %w", err)
	}

	// Получаем дополнительные данные
	owner, _ := s.objectRepository.GetOwner(id)
	touristRating, _ := s.objectRepository.GetTouristRating(id)
	entrepreneurRating, _ := s.objectRepository.GetEntrepreneurRating(id)
	feedbacks, _ := s.objectRepository.GetFeedbacks(id, 0, 5) // Последние 5 отзывов

	return s.mapToObjectResponse(object, owner, touristRating, entrepreneurRating, feedbacks), nil
}

// CreateObject создает новый объект
func (s *objectServiceImpl) CreateObject(ctx context.Context, req *CreateObjectRequest) (*ObjectResponse, error) {
	// Валидация
	if err := s.validateCreateRequest(req); err != nil {
		return nil, err
	}

	// Устанавливаем значения по умолчанию
	isActive := true
	if req.IsActive != nil {
		isActive = *req.IsActive
	}

	isVerified := false
	if req.IsVerified != nil {
		isVerified = *req.IsVerified
	}

	object := &models.Object{
		OwnerID:          req.OwnerID,
		ShortName:        req.ShortName,
		LongName:         req.LongName,
		Type:             req.Type,
		Phone:            req.Phone,
		Email:            req.Email,
		Site:             req.Site,
		ShortDescription: req.ShortDescription,
		Description:      req.Description,
		Address:          req.Address,
		Latitude:         req.Latitude,
		Longitude:        req.Longitude,
		IsActive:         isActive,
		IsVerified:       isVerified,
		FeedbackCount:    0,
	}

	if err := s.objectRepository.Create(object); err != nil {
		return nil, fmt.Errorf("failed to create object: %w", err)
	}

	// Создаем начальные записи рейтингов
	s.initializeRatings(object.ID)

	return s.GetObjectByID(ctx, object.ID)
}

// UpdateObject обновляет существующий объект
func (s *objectServiceImpl) UpdateObject(ctx context.Context, id uint, req *UpdateObjectRequest) (*ObjectResponse, error) {
	// Проверяем существование объекта
	existing, err := s.objectRepository.GetByID(id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrObjectNotFound
		}
		return nil, fmt.Errorf("failed to get object: %w", err)
	}

	// Применяем изменения
	s.applyUpdates(existing, req)

	if err := s.objectRepository.Update(existing); err != nil {
		return nil, fmt.Errorf("failed to update object: %w", err)
	}

	return s.GetObjectByID(ctx, id)
}

// DeleteObject мягко удаляет объект
func (s *objectServiceImpl) DeleteObject(ctx context.Context, id uint) error {
	// Проверяем существование
	if _, err := s.objectRepository.GetByID(id); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrObjectNotFound
		}
		return fmt.Errorf("failed to get object: %w", err)
	}

	if err := s.objectRepository.Delete(id); err != nil {
		return fmt.Errorf("failed to delete object: %w", err)
	}

	return nil
}

// ListObjects возвращает список объектов с пагинацией и фильтрацией
func (s *objectServiceImpl) ListObjects(ctx context.Context, req *ListObjectsRequest) (*ObjectListResponse, error) {
	// Устанавливаем значения по умолчанию
	page := req.Page
	if page < 1 {
		page = 1
	}

	pageSize := req.PageSize
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize

	var objects []models.Object
	var total int64
	var err error

	// Применяем фильтры
	switch {
	case req.Type != "":
		objects, err = s.objectRepository.ListByType(req.Type, offset, pageSize)
		if err == nil {
			total, _ = s.countObjectsByType(req.Type)
		}
	case req.Status != nil:
		objects, err = s.objectRepository.ListByStatus(*req.Status, offset, pageSize)
		if err == nil {
			total, _ = s.countObjectsByStatus(*req.Status)
		}
	case req.Query != "":
		objects, err = s.objectRepository.Search(req.Query, offset, pageSize)
		if err == nil {
			total, _ = s.countObjectsBySearch(req.Query)
		}
	default:
		objects, err = s.objectRepository.List(offset, pageSize)
		if err == nil {
			total, _ = s.objectRepository.Count()
		}
	}

	if err != nil {
		return nil, fmt.Errorf("failed to list objects: %w", err)
	}

	items := make([]ObjectShortResponse, len(objects))
	for i, obj := range objects {
		items[i] = s.mapToObjectShortResponse(&obj)
	}

	totalPages := int(total) / pageSize
	if int(total)%pageSize > 0 {
		totalPages++
	}

	return &ObjectListResponse{
		Items:      items,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// GetObjectsByOwner возвращает объекты владельца
func (s *objectServiceImpl) GetObjectsByOwner(ctx context.Context, ownerID uint, page, pageSize int) (*ObjectListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize
	objects, err := s.objectRepository.ListByOwner(ownerID, offset, pageSize)
	if err != nil {
		return nil, fmt.Errorf("failed to get objects by owner: %w", err)
	}

	total, _ := s.countObjectsByOwner(ownerID)

	items := make([]ObjectShortResponse, len(objects))
	for i, obj := range objects {
		items[i] = s.mapToObjectShortResponse(&obj)
	}

	totalPages := int(total) / pageSize
	if int(total)%pageSize > 0 {
		totalPages++
	}

	return &ObjectListResponse{
		Items:      items,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// GetObjectsByType возвращает объекты по типу
func (s *objectServiceImpl) GetObjectsByType(ctx context.Context, objectType string, page, pageSize int) (*ObjectListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize
	objects, err := s.objectRepository.ListByType(objectType, offset, pageSize)
	if err != nil {
		return nil, fmt.Errorf("failed to get objects by type: %w", err)
	}

	total, _ := s.countObjectsByType(objectType)

	items := make([]ObjectShortResponse, len(objects))
	for i, obj := range objects {
		items[i] = s.mapToObjectShortResponse(&obj)
	}

	totalPages := int(total) / pageSize
	if int(total)%pageSize > 0 {
		totalPages++
	}

	return &ObjectListResponse{
		Items:      items,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// SearchObjects ищет объекты по запросу
func (s *objectServiceImpl) SearchObjects(ctx context.Context, query string, page, pageSize int) (*ObjectListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize
	objects, err := s.objectRepository.Search(query, offset, pageSize)
	if err != nil {
		return nil, fmt.Errorf("failed to search objects: %w", err)
	}

	total, _ := s.countObjectsBySearch(query)

	items := make([]ObjectShortResponse, len(objects))
	for i, obj := range objects {
		items[i] = s.mapToObjectShortResponse(&obj)
	}

	totalPages := int(total) / pageSize
	if int(total)%pageSize > 0 {
		totalPages++
	}

	return &ObjectListResponse{
		Items:      items,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// GetNearbyObjects возвращает объекты в радиусе
func (s *objectServiceImpl) GetNearbyObjects(ctx context.Context, lat, lng, radius float64, page, pageSize int) (*ObjectListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize
	objects, err := s.objectRepository.GetNearby(lat, lng, radius, offset, pageSize)
	if err != nil {
		return nil, fmt.Errorf("failed to get nearby objects: %w", err)
	}

	items := make([]ObjectShortResponse, len(objects))
	for i, obj := range objects {
		items[i] = s.mapToObjectShortResponse(&obj)
	}

	return &ObjectListResponse{
		Items:      items,
		Total:      int64(len(objects)),
		Page:       page,
		PageSize:   pageSize,
		TotalPages: 1,
	}, nil
}

// ToggleVerification переключает статус верификации
func (s *objectServiceImpl) ToggleVerification(ctx context.Context, id uint, verified bool) error {
	if err := s.objectRepository.ToggleVerification(id, verified); err != nil {
		return fmt.Errorf("failed to toggle verification: %w", err)
	}
	return nil
}

// CreateFeedback создает отзыв
func (s *objectServiceImpl) CreateFeedback(ctx context.Context, req *CreateFeedbackRequest, ownerID uint) (*FeedbackResponse, error) {
	// Проверяем существование объекта
	if _, err := s.objectRepository.GetByID(req.ObjectID); err != nil {
		return nil, ErrObjectNotFound
	}

	feedback := &models.Feedback{
		OwnerID:  ownerID,
		ObjectID: req.ObjectID,
		Platform: req.Platform,
		Score:    req.Score,
		Text:     req.Text,
	}

	// TODO: Добавить метод CreateFeedback в репозиторий
	// if err := s.objectRepository.CreateFeedback(feedback); err != nil {
	//     return nil, fmt.Errorf("failed to create feedback: %w", err)
	// }

	// Обновляем счетчик отзывов
	if err := s.objectRepository.UpdateFeedbackCount(req.ObjectID, 1); err != nil {
		// Логируем ошибку, но не прерываем выполнение
		fmt.Printf("Failed to update feedback count: %v\n", err)
	}

	return s.GetFeedbackByID(ctx, feedback.ID)
}

// UpdateFeedback обновляет отзыв
func (s *objectServiceImpl) UpdateFeedback(ctx context.Context, id uint, req *UpdateFeedbackRequest, ownerID uint) (*FeedbackResponse, error) {
	// TODO: Реализовать обновление отзыва
	return nil, ErrNotImplemented
}

// DeleteFeedback удаляет отзыв
func (s *objectServiceImpl) DeleteFeedback(ctx context.Context, id uint, ownerID uint) error {
	// TODO: Реализовать удаление отзыва
	return ErrNotImplemented
}

// GetFeedbackByID возвращает отзыв по ID
func (s *objectServiceImpl) GetFeedbackByID(ctx context.Context, id uint) (*FeedbackResponse, error) {
	// TODO: Добавить метод GetFeedbackByID в репозиторий
	// feedback, err := s.objectRepository.GetFeedbackByID(id)
	// if err != nil {
	//     return nil, fmt.Errorf("failed to get feedback: %w", err)
	// }

	// return s.mapToFeedbackResponse(feedback), nil
	return nil, ErrNotImplemented
}

// GetFeedbacksByObject возвращает отзывы объекта
func (s *objectServiceImpl) GetFeedbacksByObject(ctx context.Context, objectID uint, page, pageSize int) (*FeedbackListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	if pageSize > 100 {
		pageSize = 100
	}

	offset := (page - 1) * pageSize
	feedbacks, err := s.objectRepository.GetFeedbacks(objectID, offset, pageSize)
	if err != nil {
		return nil, fmt.Errorf("failed to get feedbacks: %w", err)
	}

	count, err := s.objectRepository.GetFeedbackCount(objectID)
	if err != nil {
		count = 0
	}

	items := make([]FeedbackResponse, len(feedbacks))
	for i, fb := range feedbacks {
		items[i] = *s.mapToFeedbackResponse(&fb)
	}

	totalPages := count / pageSize
	if count%pageSize > 0 {
		totalPages++
	}

	return &FeedbackListResponse{
		Items:      items,
		Total:      int64(count),
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// CreateRatingVote создает голос в рейтинге
func (s *objectServiceImpl) CreateRatingVote(ctx context.Context, req *CreateRatingVoteRequest, voterID uint) (*RatingVoteResponse, error) {
	// Проверяем, не голосовал ли уже пользователь
	existing, _ := s.GetUserRatingVote(ctx, req.TargetID, voterID, req.Platform)
	if existing != nil {
		return nil, ErrAlreadyVoted
	}

	ratingVote := &models.RatingVote{
		Platform: req.Platform,
		TargetID: req.TargetID,
		VoterID:  voterID,
		Score:    req.Score,
	}

	// TODO: Добавить метод CreateRatingVote в репозиторий
	// if err := s.objectRepository.CreateRatingVote(ratingVote); err != nil {
	//     return nil, fmt.Errorf("failed to create rating vote: %w", err)
	// }

	// Обновляем статистику рейтинга
	s.updateRatingStats(req.TargetID, req.Platform)

	return &RatingVoteResponse{
		ID:        ratingVote.ID,
		CreatedAt: ratingVote.CreatedAt,
		Platform:  ratingVote.Platform,
		TargetID:  ratingVote.TargetID,
		VoterID:   ratingVote.VoterID,
		Score:     ratingVote.Score,
	}, nil
}

// GetObjectRating возвращает рейтинг объекта
func (s *objectServiceImpl) GetObjectRating(ctx context.Context, objectID uint, platform models.PlatformType) (*RatingResponse, error) {
	var rating *models.Rating
	var err error

	if platform == models.PlatformTourist {
		rating, err = s.objectRepository.GetTouristRating(objectID)
	} else {
		rating, err = s.objectRepository.GetEntrepreneurRating(objectID)
	}

	if err != nil {
		return nil, fmt.Errorf("failed to get rating: %w", err)
	}

	return s.mapToRatingResponse(rating), nil
}

// GetUserRatingVote возвращает голос пользователя
func (s *objectServiceImpl) GetUserRatingVote(ctx context.Context, objectID uint, userID uint, platform models.PlatformType) (*RatingVoteResponse, error) {
	// TODO: Добавить метод GetUserRatingVote в репозиторий
	// vote, err := s.objectRepository.GetUserRatingVote(objectID, userID, platform)
	// if err != nil {
	//     return nil, err
	// }

	// return &RatingVoteResponse{
	//     ID:        vote.ID,
	//     CreatedAt: vote.CreatedAt,
	//     Platform:  vote.Platform,
	//     TargetID:  vote.TargetID,
	//     VoterID:   vote.VoterID,
	//     Score:     vote.Score,
	// }, nil
	return nil, ErrNotImplemented
}

// Вспомогательные методы

func (s *objectServiceImpl) validateCreateRequest(req *CreateObjectRequest) error {
	if req.OwnerID == 0 {
		return ErrInvalidOwnerID
	}
	if req.ShortName == "" {
		return ErrShortNameRequired
	}
	return nil
}

func (s *objectServiceImpl) applyUpdates(object *models.Object, req *UpdateObjectRequest) {
	if req.ShortName != nil {
		object.ShortName = *req.ShortName
	}
	if req.LongName != nil {
		object.LongName = *req.LongName
	}
	if req.Type != nil {
		object.Type = *req.Type
	}
	if req.Phone != nil {
		object.Phone = *req.Phone
	}
	if req.Email != nil {
		object.Email = *req.Email
	}
	if req.Site != nil {
		object.Site = *req.Site
	}
	if req.ShortDescription != nil {
		object.ShortDescription = *req.ShortDescription
	}
	if req.Description != nil {
		object.Description = *req.Description
	}
	if req.Address != nil {
		object.Address = *req.Address
	}
	if req.Latitude != nil {
		object.Latitude = *req.Latitude
	}
	if req.Longitude != nil {
		object.Longitude = *req.Longitude
	}
	if req.IsActive != nil {
		object.IsActive = *req.IsActive
	}
	if req.IsVerified != nil {
		object.IsVerified = *req.IsVerified
	}
}

func (s *objectServiceImpl) initializeRatings(objectID uint) {
	// Создаем записи рейтингов для туристической и предпринимательской платформ
	// TODO: Добавить создание рейтингов в репозиторий
}

func (s *objectServiceImpl) updateRatingStats(objectID uint, platform models.PlatformType) {
	// Обновляем статистику рейтинга
	// TODO: Реализовать обновление статистики
}

func (s *objectServiceImpl) mapToObjectResponse(object *models.Object, owner *models.Account, touristRating, entrepreneurRating *models.Rating, feedbacks []models.Feedback) *ObjectResponse {
	resp := &ObjectResponse{
		ID:               object.ID,
		CreatedAt:        object.CreatedAt,
		UpdatedAt:        object.UpdatedAt,
		OwnerID:          object.OwnerID,
		ShortName:        object.ShortName,
		LongName:         object.LongName,
		Type:             object.Type,
		Phone:            object.Phone,
		Email:            object.Email,
		Site:             object.Site,
		ShortDescription: object.ShortDescription,
		Description:      object.Description,
		Address:          object.Address,
		Latitude:         object.Latitude,
		Longitude:        object.Longitude,
		IsActive:         object.IsActive,
		IsVerified:       object.IsVerified,
		FeedbackCount:    object.FeedbackCount,
	}

	if object.DeletedAt.Valid {
		resp.DeletedAt = &object.DeletedAt.Time
	}

	if owner != nil {
		resp.Owner = &account.AccountResponse{
			ID:       owner.ID,
			FullName: owner.FullName,
			Email:    owner.Email,
			// Добавьте другие поля
		}
	}

	if touristRating != nil {
		resp.TouristRating = s.mapToRatingResponse(touristRating)
	}

	if entrepreneurRating != nil {
		resp.EntrepreneurRating = s.mapToRatingResponse(entrepreneurRating)
	}

	if len(feedbacks) > 0 {
		resp.Feedbacks = make([]FeedbackShortResponse, len(feedbacks))
		for i, fb := range feedbacks {
			resp.Feedbacks[i] = FeedbackShortResponse{
				ID:        fb.ID,
				CreatedAt: fb.CreatedAt,
				OwnerID:   fb.OwnerID,
				Platform:  fb.Platform,
				Score:     fb.Score,
				Text:      fb.Text,
			}
		}
	}

	return resp
}

func (s *objectServiceImpl) mapToObjectShortResponse(object *models.Object) ObjectShortResponse {
	return ObjectShortResponse{
		ID:            object.ID,
		ShortName:     object.ShortName,
		LongName:      object.LongName,
		Type:          object.Type,
		Address:       object.Address,
		IsActive:      object.IsActive,
		IsVerified:    object.IsVerified,
		FeedbackCount: object.FeedbackCount,
	}
}

func (s *objectServiceImpl) mapToRatingResponse(rating *models.Rating) *RatingResponse {
	return &RatingResponse{
		ID:           rating.ID,
		CreatedAt:    rating.CreatedAt,
		UpdatedAt:    rating.UpdatedAt,
		Platform:     rating.Platform,
		AverageScore: rating.AverageScore,
		TotalVotes:   rating.TotalVotes,
		VoteBreakdown: VoteBreakdownDTO{
			Score1: rating.VoteBreakdown.Score1,
			Score2: rating.VoteBreakdown.Score2,
			Score3: rating.VoteBreakdown.Score3,
			Score4: rating.VoteBreakdown.Score4,
			Score5: rating.VoteBreakdown.Score5,
		},
	}
}

func (s *objectServiceImpl) mapToFeedbackResponse(feedback *models.Feedback) *FeedbackResponse {
	return &FeedbackResponse{
		ID:           feedback.ID,
		CreatedAt:    feedback.CreatedAt,
		UpdatedAt:    feedback.UpdatedAt,
		OwnerID:      feedback.OwnerID,
		ObjectID:     feedback.ObjectID,
		Platform:     feedback.Platform,
		Score:        feedback.Score,
		Text:         feedback.Text,
		CommentCount: feedback.CommentCount,
	}
}

// Методы для подсчета (временные, должны быть в репозитории)
func (s *objectServiceImpl) countObjectsByType(objectType string) (int64, error) {
	// TODO: Добавить метод CountByType в репозиторий
	return 0, nil
}

func (s *objectServiceImpl) countObjectsByStatus(isActive bool) (int64, error) {
	// TODO: Добавить метод CountByStatus в репозиторий
	return 0, nil
}

func (s *objectServiceImpl) countObjectsByOwner(ownerID uint) (int64, error) {
	// TODO: Добавить метод CountByOwner в репозиторий
	return 0, nil
}

func (s *objectServiceImpl) countObjectsBySearch(query string) (int64, error) {
	// TODO: Добавить метод CountBySearch в репозиторий
	return 0, nil
}
