package sync

import (
	"context"
	"fmt"
	"time"

	"git.leaktechnologies.dev/stu/Goondex/internal/db"
	"git.leaktechnologies.dev/stu/Goondex/internal/scraper/tpdb"
)

// Service handles synchronization operations
type Service struct {
	db      *db.DB
	scraper *tpdb.Scraper
}

// NewService creates a new sync service
func NewService(database *db.DB, scraper *tpdb.Scraper) *Service {
	return &Service{
		db:      database,
		scraper: scraper,
	}
}

// SyncOptions configures sync behavior
type SyncOptions struct {
	Force       bool          // Force sync even if rate limit not met
	MinInterval time.Duration // Minimum time between syncs
}

// DefaultSyncOptions returns default sync options (1 hour minimum)
func DefaultSyncOptions() SyncOptions {
	return SyncOptions{
		Force:       false,
		MinInterval: 1 * time.Hour,
	}
}

// SyncResult contains the results of a sync operation
type SyncResult struct {
	EntityType     string
	Updated        int
	Failed         int
	Skipped        int
	Duration       time.Duration
	ErrorMessage   string
}

// SyncAll syncs all entity types (performers, studios, scenes)
func (s *Service) SyncAll(ctx context.Context, opts SyncOptions) ([]SyncResult, error) {
	var results []SyncResult

	// Sync performers
	performerResult, err := s.SyncPerformers(ctx, opts)
	if err != nil {
		return results, fmt.Errorf("failed to sync performers: %w", err)
	}
	results = append(results, performerResult)

	// Sync studios
	studioResult, err := s.SyncStudios(ctx, opts)
	if err != nil {
		return results, fmt.Errorf("failed to sync studios: %w", err)
	}
	results = append(results, studioResult)

	// Sync scenes
	sceneResult, err := s.SyncScenes(ctx, opts)
	if err != nil {
		return results, fmt.Errorf("failed to sync scenes: %w", err)
	}
	results = append(results, sceneResult)

	return results, nil
}

// SyncPerformers syncs all performers from TPDB
func (s *Service) SyncPerformers(ctx context.Context, opts SyncOptions) (SyncResult, error) {
	result := SyncResult{EntityType: "performers"}
	start := time.Now()
	defer func() { result.Duration = time.Since(start) }()

	syncStore := db.NewSyncStore(s.db)

	// Check rate limiting
	if !opts.Force {
		canSync, nextAllowed, err := syncStore.CanSync("performers", opts.MinInterval)
		if err != nil {
			return result, err
		}
		if !canSync {
			result.Skipped = 1
			result.ErrorMessage = fmt.Sprintf("Rate limit: next sync allowed at %s", nextAllowed.Format(time.RFC3339))
			return result, nil
		}
	}

	// Record sync start
	if err := syncStore.RecordSyncStart("performers"); err != nil {
		return result, err
	}

	performerStore := db.NewPerformerStore(s.db)

	// Get all performers with TPDB source
	performers, err := performerStore.Search("")
	if err != nil {
		syncStore.RecordSyncError("performers", err.Error())
		return result, err
	}

	// Update each performer
	for _, p := range performers {
		if p.Source != "tpdb" || p.SourceID == "" {
			result.Skipped++
			continue
		}

		// Fetch updated data from TPDB
		updated, err := s.scraper.GetPerformerByID(ctx, p.SourceID)
		if err != nil {
			fmt.Printf("⚠ Failed to fetch performer %s (ID: %d): %v\n", p.Name, p.ID, err)
			result.Failed++
			continue
		}

		// Preserve local ID
		updated.ID = p.ID

		// Update in database
		if err := performerStore.Update(updated); err != nil {
			fmt.Printf("⚠ Failed to update performer %s (ID: %d): %v\n", p.Name, p.ID, err)
			result.Failed++
			continue
		}

		result.Updated++
	}

	// Record completion
	if err := syncStore.RecordSyncComplete("performers", result.Updated, result.Failed, result.ErrorMessage); err != nil {
		return result, err
	}

	return result, nil
}

// SyncStudios syncs all studios from TPDB
func (s *Service) SyncStudios(ctx context.Context, opts SyncOptions) (SyncResult, error) {
	result := SyncResult{EntityType: "studios"}
	start := time.Now()
	defer func() { result.Duration = time.Since(start) }()

	syncStore := db.NewSyncStore(s.db)

	// Check rate limiting
	if !opts.Force {
		canSync, nextAllowed, err := syncStore.CanSync("studios", opts.MinInterval)
		if err != nil {
			return result, err
		}
		if !canSync {
			result.Skipped = 1
			result.ErrorMessage = fmt.Sprintf("Rate limit: next sync allowed at %s", nextAllowed.Format(time.RFC3339))
			return result, nil
		}
	}

	// Record sync start
	if err := syncStore.RecordSyncStart("studios"); err != nil {
		return result, err
	}

	studioStore := db.NewStudioStore(s.db)

	// Get all studios with TPDB source
	studios, err := studioStore.Search("")
	if err != nil {
		syncStore.RecordSyncError("studios", err.Error())
		return result, err
	}

	// Update each studio
	for _, st := range studios {
		if st.Source != "tpdb" || st.SourceID == "" {
			result.Skipped++
			continue
		}

		// Fetch updated data from TPDB
		updated, err := s.scraper.GetStudioByID(ctx, st.SourceID)
		if err != nil {
			fmt.Printf("⚠ Failed to fetch studio %s (ID: %d): %v\n", st.Name, st.ID, err)
			result.Failed++
			continue
		}

		// Preserve local ID
		updated.ID = st.ID

		// Update in database
		if err := studioStore.Update(updated); err != nil {
			fmt.Printf("⚠ Failed to update studio %s (ID: %d): %v\n", st.Name, st.ID, err)
			result.Failed++
			continue
		}

		result.Updated++
	}

	// Record completion
	if err := syncStore.RecordSyncComplete("studios", result.Updated, result.Failed, result.ErrorMessage); err != nil {
		return result, err
	}

	return result, nil
}

// SyncScenes syncs all scenes from TPDB
func (s *Service) SyncScenes(ctx context.Context, opts SyncOptions) (SyncResult, error) {
	result := SyncResult{EntityType: "scenes"}
	start := time.Now()
	defer func() { result.Duration = time.Since(start) }()

	syncStore := db.NewSyncStore(s.db)

	// Check rate limiting
	if !opts.Force {
		canSync, nextAllowed, err := syncStore.CanSync("scenes", opts.MinInterval)
		if err != nil {
			return result, err
		}
		if !canSync {
			result.Skipped = 1
			result.ErrorMessage = fmt.Sprintf("Rate limit: next sync allowed at %s", nextAllowed.Format(time.RFC3339))
			return result, nil
		}
	}

	// Record sync start
	if err := syncStore.RecordSyncStart("scenes"); err != nil {
		return result, err
	}

	sceneStore := db.NewSceneStore(s.db)

	// Get all scenes with TPDB source
	scenes, err := sceneStore.Search("")
	if err != nil {
		syncStore.RecordSyncError("scenes", err.Error())
		return result, err
	}

	// Update each scene
	for _, sc := range scenes {
		if sc.Source != "tpdb" || sc.SourceID == "" {
			result.Skipped++
			continue
		}

		// Fetch updated data from TPDB
		updated, err := s.scraper.GetSceneByID(ctx, sc.SourceID)
		if err != nil {
			fmt.Printf("⚠ Failed to fetch scene %s (ID: %d): %v\n", sc.Title, sc.ID, err)
			result.Failed++
			continue
		}

		// Preserve local ID
		updated.ID = sc.ID

		// Update in database
		if err := sceneStore.Update(updated); err != nil {
			fmt.Printf("⚠ Failed to update scene %s (ID: %d): %v\n", sc.Title, sc.ID, err)
			result.Failed++
			continue
		}

		result.Updated++
	}

	// Record completion
	if err := syncStore.RecordSyncComplete("scenes", result.Updated, result.Failed, result.ErrorMessage); err != nil {
		return result, err
	}

	return result, nil
}

// GetSyncStatus returns the current sync status for all entity types
func (s *Service) GetSyncStatus() ([]db.SyncMetadata, error) {
	syncStore := db.NewSyncStore(s.db)
	return syncStore.GetAllSyncStatus()
}
