package ui

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"qtransfer/internal/model"
)

func ResolveSelection(lib model.Library, allPlaylists, includeLiked, nonInteractive bool, names []string) ([]model.Playlist, bool, error) {
	if allPlaylists || len(names) > 0 || nonInteractive {
		selected, liked, err := selectByFlags(lib, allPlaylists, includeLiked, names)
		if err != nil {
			return nil, false, err
		}
		if nonInteractive && len(selected) == 0 && !liked {
			return nil, false, fmt.Errorf("no selection provided in non-interactive mode (use --all, --playlist, or --liked)")
		}
		if len(selected) == 0 && !liked && !nonInteractive {
			return nil, false, fmt.Errorf("no playlists selected")
		}
		return selected, liked, nil
	}

	return interactiveSelection(lib)
}

func selectByFlags(lib model.Library, allPlaylists, includeLiked bool, names []string) ([]model.Playlist, bool, error) {
	if allPlaylists {
		return lib.Playlists, includeLiked, nil
	}

	if len(names) == 0 {
		return nil, includeLiked, nil
	}

	lookup := map[string]model.Playlist{}
	for _, p := range lib.Playlists {
		lookup[strings.ToLower(strings.TrimSpace(p.Name))] = p
	}

	selected := make([]model.Playlist, 0, len(names))
	missing := []string{}
	seen := map[string]struct{}{}
	for _, n := range names {
		k := strings.ToLower(strings.TrimSpace(n))
		p, ok := lookup[k]
		if !ok {
			missing = append(missing, n)
			continue
		}
		if _, exists := seen[p.SourceID]; exists {
			continue
		}
		seen[p.SourceID] = struct{}{}
		selected = append(selected, p)
	}

	if len(missing) > 0 {
		return nil, false, fmt.Errorf("playlist(s) not found: %s", strings.Join(missing, ", "))
	}

	return selected, includeLiked, nil
}

func interactiveSelection(lib model.Library) ([]model.Playlist, bool, error) {
	fmt.Println("Fetched Spotify data:")
	for i, p := range lib.Playlists {
		fmt.Printf("  %2d) %s (%d tracks)\n", i+1, p.Name, len(p.Tracks))
	}
	fmt.Printf("   L) %s (%d tracks)\n", lib.LikedName, len(lib.LikedSongs))
	fmt.Println("\nSelect playlists to transfer. Examples: 1,2,5-8,L   or   A for all playlists + liked songs")

	scanner := bufio.NewScanner(os.Stdin)
	for {
		fmt.Print("Selection: ")
		if !scanner.Scan() {
			if scanner.Err() != nil {
				return nil, false, scanner.Err()
			}
			return nil, false, fmt.Errorf("input closed")
		}
		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}

		idxs, liked, all, err := parseSelection(line, len(lib.Playlists))
		if err != nil {
			fmt.Printf("Invalid selection: %v\n", err)
			continue
		}

		if all {
			return lib.Playlists, true, nil
		}

		selected := make([]model.Playlist, 0, len(idxs))
		for _, idx := range idxs {
			selected = append(selected, lib.Playlists[idx-1])
		}

		if len(selected) == 0 && !liked {
			fmt.Println("No playlists selected. Please choose at least one playlist or L.")
			continue
		}
		return selected, liked, nil
	}
}

func parseSelection(s string, max int) ([]int, bool, bool, error) {
	parts := strings.Split(strings.ToUpper(s), ",")
	set := map[int]struct{}{}
	liked := false
	all := false

	for _, p := range parts {
		p = strings.TrimSpace(p)
		if p == "" {
			continue
		}
		switch p {
		case "A", "ALL":
			all = true
			liked = true
			continue
		case "L", "LIKED":
			liked = true
			continue
		}

		if strings.Contains(p, "-") {
			r := strings.SplitN(p, "-", 2)
			if len(r) != 2 {
				return nil, false, false, fmt.Errorf("invalid range %q", p)
			}
			start, err := strconv.Atoi(strings.TrimSpace(r[0]))
			if err != nil {
				return nil, false, false, fmt.Errorf("invalid range start in %q", p)
			}
			end, err := strconv.Atoi(strings.TrimSpace(r[1]))
			if err != nil {
				return nil, false, false, fmt.Errorf("invalid range end in %q", p)
			}
			if start < 1 || end < 1 || start > max || end > max || end < start {
				return nil, false, false, fmt.Errorf("range out of bounds: %q", p)
			}
			for i := start; i <= end; i++ {
				set[i] = struct{}{}
			}
			continue
		}

		n, err := strconv.Atoi(p)
		if err != nil {
			return nil, false, false, fmt.Errorf("invalid token %q", p)
		}
		if n < 1 || n > max {
			return nil, false, false, fmt.Errorf("playlist index out of bounds: %d", n)
		}
		set[n] = struct{}{}
	}

	idxs := make([]int, 0, len(set))
	for i := range set {
		idxs = append(idxs, i)
	}
	sort.Ints(idxs)
	return idxs, liked, all, nil
}
