package feedback

import (
	"context"
	"tm/internal/company"
	"tm/internal/tender"
	"tm/pkg/logger"
	"tm/pkg/response"
)

// FeedbackService interface defines feedback business logic methods
type Service interface {
	Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error)
	Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error)
	Get(ctx context.Context, id string) (*FeedbackResponse, error)
	GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
	Delete(ctx context.Context, id string) error
	CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error)
	getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error)
}

// feedbackService implements FeedbackService interface
type feedbackService struct {
	feedbackRepo   Repository
	tenderService  tender.Service
	companyService company.Service
	logger         logger.Logger
}

// NewService creates a new feedback service
func NewService(feedbackRepo Repository, tenderService tender.Service, companyService company.Service, logger logger.Logger) Service {
	return &feedbackService{
		feedbackRepo:   feedbackRepo,
		tenderService:  tenderService,
		companyService: companyService,
		logger:         logger,
	}
}

func (s *feedbackService) Toggle(ctx context.Context, req ToggleFeedbackForm, customerID *string, companyID *string) (*FeedbackResponse, error) {
	existing, err := s.feedbackRepo.GetByCompanyID(ctx, *companyID, req.Tender)

	if err != nil {
		feedback := &Feedback{
			TenderID:     req.Tender,
			FeedbackType: req.Type,
			CompanyID:    companyID,
			CustomerID:   customerID,
		}

		err = s.feedbackRepo.Create(ctx, feedback)
		if err != nil {
			s.logger.Error("Failed to create new feedback", map[string]interface{}{
				"tender_id":  req.Tender,
				"company_id": *companyID,
				"error":      err.Error(),
			})
			return nil, err
		}

		tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
		if err != nil {
			s.logger.Error("Failed to get details", map[string]interface{}{
				"tender_id":  req.Tender,
				"company_id": *companyID,
				"error":      err.Error(),
			})
		}

		return feedback.ToResponse(tender, company), nil
	}

	if existing.FeedbackType == req.Type {
		err = s.feedbackRepo.Delete(ctx, existing.GetID())
		if err != nil {
			s.logger.Error("Failed to update existing feedback", map[string]interface{}{
				"feedback_id": existing.GetID(),
				"tender_id":   req.Tender,
				"company_id":  *companyID,
				"error":       err.Error(),
			})
			return nil, err
		}

		if req.Type == FeedbackTypeLike {
			existing.FeedbackType = "unlike"
		} else {
			existing.FeedbackType = "undislike"
		}

		tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
		if err != nil {
			s.logger.Error("Failed to get details", map[string]interface{}{
				"tender_id":  req.Tender,
				"company_id": *companyID,
				"error":      err.Error(),
			})
		}

		return existing.ToResponse(tender, company), nil
	}

	if existing.FeedbackType == FeedbackTypeLike {
		existing.FeedbackType = FeedbackTypeDislike
	} else {
		existing.FeedbackType = FeedbackTypeLike
	}

	err = s.feedbackRepo.Update(ctx, existing)
	if err != nil {
		s.logger.Error("Failed to update existing feedback", map[string]interface{}{
			"feedback_id": existing.GetID(),
			"tender_id":   req.Tender,
			"company_id":  *companyID,
			"error":       err.Error(),
		})
		return nil, err
	}

	tender, company, err := s.getDetails(ctx, req.Tender, *companyID)
	if err != nil {
		s.logger.Error("Failed to get details", map[string]interface{}{
			"tender_id":  req.Tender,
			"company_id": *companyID,
			"error":      err.Error(),
		})
	}

	return existing.ToResponse(tender, company), nil
}

func (s *feedbackService) Get(ctx context.Context, id string) (*FeedbackResponse, error) {
	feedback, err := s.feedbackRepo.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to get feedback", map[string]interface{}{
			"id":    id,
			"error": err.Error(),
		})
		return nil, err
	}

	tender, company, err := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID)
	if err != nil {
		s.logger.Error("Failed to get details", map[string]interface{}{
			"tender_id":  feedback.TenderID,
			"company_id": *feedback.CompanyID,
			"error":      err.Error(),
		})
	}

	return feedback.ToResponse(tender, company), nil
}

func (s *feedbackService) Search(ctx context.Context, criteria SearchForm, pagination *response.Pagination) (*FeedbackListResponse, error) {
	feedbacks, total, err := s.feedbackRepo.Search(ctx, criteria, pagination)
	if err != nil {
		s.logger.Error("Failed to list feedback", map[string]interface{}{
			"criteria": criteria,
			"limit":    pagination.Limit,
			"offset":   pagination.Offset,
			"error":    err.Error(),
		})
		return nil, err
	}

	meta := &response.Meta{
		Total:  int(total),
		Limit:  pagination.Limit,
		Offset: pagination.Offset,
		Page:   (pagination.Offset / pagination.Limit) + 1,
		Pages:  int((total + int64(pagination.Limit) - 1) / int64(pagination.Limit)),
	}

	result := make([]*FeedbackResponse, len(feedbacks))
	for i, feedback := range feedbacks {
		tender, company, _ := s.getDetails(ctx, feedback.TenderID, *feedback.CompanyID)
		result[i] = feedback.ToResponse(tender, company)
	}

	return &FeedbackListResponse{
		Feedback: result,
		Meta:     meta,
	}, nil
}

func (s *feedbackService) Delete(ctx context.Context, id string) error {
	return s.feedbackRepo.Delete(ctx, id)
}

func (s *feedbackService) GetByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error) {
	feedback, err := s.feedbackRepo.GetByTenderID(ctx, tenderID, companyID)
	if err != nil {
		s.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{
			"tender_id": tenderID,
			"error":     err.Error(),
		})
		return nil, err
	}

	tender, err := s.tenderService.GetByID(ctx, feedback.TenderID)
	if err != nil {
		s.logger.Error("Failed to get tender", map[string]interface{}{
			"tender_id": feedback.TenderID,
			"error":     err.Error(),
		})
	}

	company, err := s.companyService.GetByID(ctx, *feedback.CompanyID)
	if err != nil {
		s.logger.Error("Failed to get company", map[string]interface{}{
			"company_id": feedback.CompanyID,
			"error":      err.Error(),
		})
	}

	return feedback.ToResponse(
		&tenderResponse{
			ID:                  tender.ID,
			NoticePublicationID: tender.NoticePublicationID,
			PublicationDate:     tender.PublicationDate,
			Title:               tender.Title,
			Description:         tender.Description,
			ProcurementTypeCode: tender.ProcurementTypeCode,
			ProcedureCode:       tender.ProcedureCode,
			EstimatedValue:      tender.EstimatedValue,
			Currency:            tender.Currency,
			Duration:            tender.Duration,
			DurationUnit:        tender.DurationUnit,
			TenderDeadline:      tender.TenderDeadline,
			SubmissionDeadline:  tender.SubmissionDeadline,
			CountryCode:         tender.CountryCode,
			BuyerOrganization:   &organizationResponse{Name: tender.BuyerOrganization.Name},
			Status:              string(tender.Status),
		},
		&companyResponse{
			ID:   company.ID,
			Name: company.Name,
		},
	), nil
}

func (s *feedbackService) getDetails(ctx context.Context, tenderID, companyID string) (*tenderResponse, *companyResponse, error) {
	tender, err := s.tenderService.GetByID(ctx, tenderID)
	if err != nil {
		s.logger.Error("Failed to get tender", map[string]interface{}{
			"tender_id": tenderID,
			"error":     err.Error(),
		})
	}

	company, err := s.companyService.GetByID(ctx, companyID)
	if err != nil {
		s.logger.Error("Failed to get company", map[string]interface{}{
			"company_id": companyID,
			"error":      err.Error(),
		})
	}

	return &tenderResponse{
			ID:                  tender.ID,
			NoticePublicationID: tender.NoticePublicationID,
			PublicationDate:     tender.PublicationDate,
			Title:               tender.Title,
			Description:         tender.Description,
			ProcurementTypeCode: tender.ProcurementTypeCode,
			ProcedureCode:       tender.ProcedureCode,
			EstimatedValue:      tender.EstimatedValue,
			Currency:            tender.Currency,
			Duration:            tender.Duration,
			DurationUnit:        tender.DurationUnit,
			TenderDeadline:      tender.TenderDeadline,
			SubmissionDeadline:  tender.SubmissionDeadline,
			CountryCode:         tender.CountryCode,
			BuyerOrganization:   &organizationResponse{Name: tender.BuyerOrganization.Name},
			Status:              string(tender.Status),
		}, &companyResponse{
			ID:   company.ID,
			Name: company.Name,
		}, nil
}

func (s *feedbackService) CompanyStats(ctx context.Context, companyID string) (*StatsResponse, error) {
	s.logger.Info("Getting company feedback statistics", map[string]interface{}{
		"company_id": companyID,
	})

	stats, err := s.feedbackRepo.CompanyStats(ctx, companyID)
	if err != nil {
		s.logger.Error("Failed to get company stats", map[string]interface{}{
			"company_id": companyID,
			"error":      err.Error(),
		})
		return nil, err
	}

	s.logger.Info("Company stats retrieved successfully", map[string]interface{}{
		"company_id":     companyID,
		"total_feedback": stats.TotalFeedback,
		"total_likes":    stats.TotalLikes,
		"total_dislikes": stats.TotalDislikes,
	})

	return stats, nil
}
