package cmd

import (
	"fmt"
	"os"
	"strconv"

	"github.com/spf13/cobra"
	"github.com/yeho/klp/internal/clipboard"
	"github.com/yeho/klp/internal/config"
	"github.com/yeho/klp/internal/database"
	"github.com/yeho/klp/internal/search"
	"github.com/yeho/klp/internal/tui"
)

var (
	Version = "dev" // Set at build time via ldflags

	versionFlag   bool
	listFlag      bool
	listLimitFlag int
	limitFlag     int
	searchFlag    string
	deleteFlag    string

	cfg *config.Config
)

var rootCmd = &cobra.Command{
	Use:   "klp [id]",
	Short: "A clipboard history manager for Linux",
	Long: `klp is a clipboard history manager that monitors clipboard changes
and provides quick access to your clipboard history.`,
	Args: cobra.MaximumNArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		if versionFlag {
			fmt.Println(Version)
			return
		}

		if deleteFlag != "" {
			deleteEntry(deleteFlag)
			return
		}

		if searchFlag != "" {
			searchEntries(searchFlag)
			return
		}

		// If ID provided as argument, copy that entry to clipboard
		if len(args) == 1 {
			copyEntryToClipboard(args[0])
			return
		}

		if listFlag {
			// Non-interactive list
			limit := cfg.DefaultLimit
			if listLimitFlag > 0 {
				limit = listLimitFlag
			}
			listEntriesNonInteractive(limit)
			return
		}

		// Default: interactive list
		limit := cfg.DefaultLimit
		if limitFlag > 0 {
			limit = limitFlag
		}
		listEntriesInteractive(limit)
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	rootCmd.Flags().BoolVarP(&versionFlag, "version", "v", false, "Print version")
	rootCmd.Flags().BoolVarP(&listFlag, "list", "l", false, "Non-interactive list output")
	rootCmd.Flags().IntVarP(&listLimitFlag, "list-limit", "n", 0, "Limit for non-interactive list (use with -l)")
	rootCmd.Flags().IntVar(&limitFlag, "limit", 0, "Limit for interactive list")
	rootCmd.Flags().StringVarP(&searchFlag, "search", "s", "", "Search clipboard entries")
	rootCmd.Flags().StringVarP(&deleteFlag, "delete", "d", "", "Delete entry by ID")
}

func initConfig() {
	var err error
	cfg, err = config.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading config: %v\n", err)
		os.Exit(1)
	}
}

func loadDatabase() *database.Database {
	db, err := database.Load(cfg.DBPath())
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading database: %v\n", err)
		os.Exit(1)
	}
	return db
}

func copyEntryToClipboard(id string) {
	db := loadDatabase()

	entry, err := db.Get(id)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Entry not found: %s\n", id)
		os.Exit(1)
	}

	clip, err := clipboard.New()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error initializing clipboard: %v\n", err)
		os.Exit(1)
	}

	if err := clip.Write(entry.Value); err != nil {
		fmt.Fprintf(os.Stderr, "Error writing to clipboard: %v\n", err)
		os.Exit(1)
	}

	preview := entry.Value
	if len(preview) > 50 {
		preview = preview[:50] + "..."
	}
	fmt.Printf("Copied to clipboard: %s\n", cleanForDisplay(preview))
}

func deleteEntry(id string) {
	db := loadDatabase()

	if err := db.Delete(id); err != nil {
		fmt.Fprintf(os.Stderr, "Error deleting entry: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Deleted entry: %s\n", id)
}

func searchEntries(query string) {
	db := loadDatabase()
	searcher := search.NewSearcher(db)

	limit := cfg.DefaultLimit
	if limitFlag > 0 {
		limit = limitFlag
	}

	results := searcher.Search(query, search.Options{MaxResults: limit})

	if len(results) == 0 {
		fmt.Printf("No results found for '%s'\n", query)
		return
	}

	// Convert results to entries for display
	entries := make([]*database.Entry, len(results))
	for i, r := range results {
		entries[i] = r.Entry
	}

	if listFlag {
		// Non-interactive search results
		printEntries(entries)
		return
	}

	// Interactive search results
	title := fmt.Sprintf("Search results for '%s' (%d found)", query, len(results))
	selected, err := tui.Run(entries, db, title)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error running TUI: %v\n", err)
		os.Exit(1)
	}

	if selected != nil {
		copyToClipboard(selected)
	}
}

func listEntriesNonInteractive(limit int) {
	db := loadDatabase()
	entries := db.List(limit)

	if len(entries) == 0 {
		fmt.Println("No clipboard entries found")
		return
	}

	printEntries(entries)
}

func listEntriesInteractive(limit int) {
	db := loadDatabase()
	entries := db.List(limit)

	if len(entries) == 0 {
		fmt.Println("No clipboard entries found")
		return
	}

	title := fmt.Sprintf("Clipboard History (%d entries)", len(entries))
	selected, err := tui.Run(entries, db, title)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error running TUI: %v\n", err)
		os.Exit(1)
	}

	if selected != nil {
		copyToClipboard(selected)
	}
}

func printEntries(entries []*database.Entry) {
	for i, entry := range entries {
		preview := entry.Value
		if len(preview) > 60 {
			preview = preview[:60] + "..."
		}
		preview = cleanForDisplay(preview)

		fmt.Printf("%s. [%s] %s  %s\n",
			padLeft(strconv.Itoa(i+1), 2),
			entry.ID,
			entry.DateTime.Format("01/02 15:04"),
			preview,
		)
	}
}

func copyToClipboard(entry *database.Entry) {
	clip, err := clipboard.New()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error initializing clipboard: %v\n", err)
		os.Exit(1)
	}

	if err := clip.Write(entry.Value); err != nil {
		fmt.Fprintf(os.Stderr, "Error writing to clipboard: %v\n", err)
		os.Exit(1)
	}

	preview := entry.Value
	if len(preview) > 50 {
		preview = preview[:50] + "..."
	}
	fmt.Printf("Copied to clipboard: %s\n", cleanForDisplay(preview))
}

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

func padLeft(s string, width int) string {
	for len(s) < width {
		s = " " + s
	}
	return s
}
