package analytics_test

import (
	"context"
	"strings"
	"testing"
	"tercul/internal/app/analytics"
	"tercul/internal/data/sql"
	"tercul/internal/domain"
	"tercul/internal/jobs/linguistics"
	"tercul/internal/platform/config"
	"tercul/internal/testutil"

	"github.com/stretchr/testify/suite"
)

// AnalyticsServiceTestSuite is a test suite for the analytics service.
// It embeds the IntegrationTestSuite to get access to the database, app, etc.
type AnalyticsServiceTestSuite struct {
	testutil.IntegrationTestSuite
	service analytics.Service
}

// SetupSuite sets up the test suite with a real database and a real analytics service.
func (s *AnalyticsServiceTestSuite) SetupSuite() {
	// Call the parent suite's setup
	s.IntegrationTestSuite.SetupSuite(nil)

	// Create a real analytics service with the test database
	cfg, err := config.LoadConfig()
	s.Require().NoError(err)
	analyticsRepo := sql.NewAnalyticsRepository(s.DB, cfg)
	analysisRepo := linguistics.NewGORMAnalysisRepository(s.DB)
	translationRepo := sql.NewTranslationRepository(s.DB, cfg)
	workRepo := sql.NewWorkRepository(s.DB, cfg)
	sentimentProvider, err := linguistics.NewGoVADERSentimentProvider()
	s.Require().NoError(err)

	// Create the service to be tested
	s.service = analytics.NewService(analyticsRepo, analysisRepo, translationRepo, workRepo, sentimentProvider)
}

// SetupTest cleans the database before each test.
func (s *AnalyticsServiceTestSuite) SetupTest() {
	s.IntegrationTestSuite.SetupTest()
	s.DB.Exec("DELETE FROM trendings")
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkViews() {
	s.Run("should increment the view count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkViews(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.Views)
	})
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkLikes() {
	s.Run("should increment the like count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkLikes(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.Likes)
	})
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkComments() {
	s.Run("should increment the comment count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkComments(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.Comments)
	})
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkBookmarks() {
	s.Run("should increment the bookmark count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkBookmarks(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.Bookmarks)
	})
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkShares() {
	s.Run("should increment the share count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkShares(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.Shares)
	})
}

func (s *AnalyticsServiceTestSuite) TestIncrementWorkTranslationCount() {
	s.Run("should increment the translation count for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")

		// Act
		err := s.service.IncrementWorkTranslationCount(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(int64(1), stats.TranslationCount)
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateWorkReadingTime() {
	s.Run("should update the reading time for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		s.DB.Create(&domain.ReadabilityScore{WorkID: work.ID})
		s.DB.Create(&domain.LanguageAnalysis{WorkID: work.ID, Analysis: domain.JSONB{}})
		textMetadata := &domain.TextMetadata{
			WorkID:    work.ID,
			WordCount: 1000,
		}
		s.DB.Create(textMetadata)

		// Act
		err := s.service.UpdateWorkReadingTime(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(5, stats.ReadingTime) // 1000 words / 200 wpm = 5 minutes
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateTranslationReadingTime() {
	s.Run("should update the reading time for a translation", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		translation := s.CreateTestTranslation(work.ID, "es", strings.Repeat("Contenido de prueba con quinientas palabras. ", 100))

		// Act
		err := s.service.UpdateTranslationReadingTime(context.Background(), translation.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateTranslationStats(context.Background(), translation.ID)
		s.Require().NoError(err)
		s.Equal(3, stats.ReadingTime) // 500 words / 200 wpm = 2.5 -> 3 minutes
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateWorkComplexity() {
	s.Run("should update the complexity for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		s.DB.Create(&domain.TextMetadata{WorkID: work.ID})
		s.DB.Create(&domain.LanguageAnalysis{WorkID: work.ID, Analysis: domain.JSONB{}})
		readabilityScore := &domain.ReadabilityScore{
			WorkID: work.ID,
			Score:  12.34,
		}
		s.DB.Create(readabilityScore)

		// Act
		err := s.service.UpdateWorkComplexity(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(12.34, stats.Complexity)
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateWorkSentiment() {
	s.Run("should update the sentiment for a work", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		s.DB.Create(&domain.TextMetadata{WorkID: work.ID})
		s.DB.Create(&domain.ReadabilityScore{WorkID: work.ID})
		languageAnalysis := &domain.LanguageAnalysis{
			WorkID: work.ID,
			Analysis: domain.JSONB{
				"sentiment": 0.5678,
			},
		}
		s.DB.Create(languageAnalysis)

		// Act
		err := s.service.UpdateWorkSentiment(context.Background(), work.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID)
		s.Require().NoError(err)
		s.Equal(0.5678, stats.Sentiment)
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateTranslationSentiment() {
	s.Run("should update the sentiment for a translation", func() {
		// Arrange
		work := s.CreateTestWork(s.AdminCtx, "Test Work", "en", "Test content")
		translation := s.CreateTestTranslation(work.ID, "en", "This is a wonderfully positive and uplifting sentence.")

		// Act
		err := s.service.UpdateTranslationSentiment(context.Background(), translation.ID)
		s.Require().NoError(err)

		// Assert
		stats, err := s.service.GetOrCreateTranslationStats(context.Background(), translation.ID)
		s.Require().NoError(err)
		s.True(stats.Sentiment > 0.5)
	})
}

func (s *AnalyticsServiceTestSuite) TestUpdateTrending() {
	s.Run("should update the trending works", func() {
		// Arrange
		work1 := s.CreateTestWork(s.AdminCtx, "Work 1", "en", "content")
		work2 := s.CreateTestWork(s.AdminCtx, "Work 2", "en", "content")
		s.DB.Create(&domain.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1})
		s.DB.Create(&domain.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10})

		// Act
		err := s.service.UpdateTrending(context.Background())
		s.Require().NoError(err)

		// Assert
		var trendingWorks []*domain.Trending
		s.DB.Order("rank asc").Find(&trendingWorks)
		s.Require().Len(trendingWorks, 2)
		s.Equal(work2.ID, trendingWorks[0].EntityID)
		s.Equal(work1.ID, trendingWorks[1].EntityID)
	})
}

// TestAnalyticsService runs the full test suite.
func TestAnalyticsService(t *testing.T) {
	suite.Run(t, new(AnalyticsServiceTestSuite))
}