package service

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/yeho/klp/internal/clipboard"
	"github.com/yeho/klp/internal/config"
	"github.com/yeho/klp/internal/database"
)

type Monitor struct {
	cfg       *config.Config
	db        *database.Database
	clipboard clipboard.Clipboard
}

func NewMonitor(cfg *config.Config, db *database.Database) (*Monitor, error) {
	clip, err := clipboard.New()
	if err != nil {
		return nil, fmt.Errorf("failed to initialize clipboard: %w", err)
	}

	return &Monitor{
		cfg:       cfg,
		db:        db,
		clipboard: clip,
	}, nil
}

func (m *Monitor) Run() error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Handle shutdown signals
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)

	go func() {
		<-sigChan
		fmt.Println("\nShutting down...")
		cancel()
	}()

	fmt.Println("Clipboard monitor started. Press Ctrl+C to stop.")

	changes := m.clipboard.Watch(ctx)
	for {
		select {
		case <-ctx.Done():
			return nil
		case content, ok := <-changes:
			if !ok {
				return nil
			}
			if err := m.handleChange(content); err != nil {
				fmt.Fprintf(os.Stderr, "Error handling clipboard change: %v\n", err)
			}
		}
	}
}

func (m *Monitor) handleChange(content string) error {
	// Skip empty content
	if content == "" {
		return nil
	}

	// Check max entry size if configured
	if m.cfg.MaxEntrySize > 0 && len(content) > m.cfg.MaxEntrySize {
		return nil
	}

	// Skip if duplicate of most recent entry
	recent := m.db.MostRecent()
	if recent != nil && recent.Value == content {
		return nil
	}

	// Add to database
	entry, err := m.db.Add(content)
	if err != nil {
		return err
	}

	// Log the addition (truncate for display)
	preview := content
	if len(preview) > 50 {
		preview = preview[:50] + "..."
	}
	// Replace newlines for cleaner display
	preview = replaceNewlines(preview)
	fmt.Printf("Added [%s]: %s\n", entry.ID, preview)

	return nil
}

func replaceNewlines(s string) string {
	result := make([]byte, 0, len(s))
	for i := 0; i < len(s); i++ {
		if s[i] == '\n' {
			result = append(result, ' ')
		} else if s[i] == '\r' {
			// skip
		} else {
			result = append(result, s[i])
		}
	}
	return string(result)
}
