package pokercore

import (
	"fmt"
	"slices"
	"sort"
	"strings"
	"unicode/utf8"

	"github.com/logrusorgru/aurora/v4"
)

type Card struct {
	Rank Rank
	Suit Suit
}

func NewCardFromString(card string) (Card, error) {
	// FIXME extend this later to common format strings like "9s"
	length := utf8.RuneCountInString(card)
	if length != 2 {
		return Card{}, fmt.Errorf("Invalid card string %s", card)
	}
	var rank Rank
	var suit Suit
	if strings.ContainsRune(card, rune(SPADE)) {
		suit = Suit(SPADE)
	} else if strings.ContainsRune(card, rune(HEART)) {
		suit = Suit(HEART)
	} else if strings.ContainsRune(card, rune(DIAMOND)) {
		suit = Suit(DIAMOND)
	} else if strings.ContainsRune(card, rune(CLUB)) {
		suit = Suit(CLUB)
	} else {
		return Card{}, fmt.Errorf("Invalid card string %s", card)
	}

	if strings.ContainsRune(card, rune(DEUCE)) {
		rank = Rank(DEUCE)
	} else if strings.ContainsRune(card, rune(THREE)) {
		rank = Rank(THREE)
	} else if strings.ContainsRune(card, rune(FOUR)) {
		rank = Rank(FOUR)
	} else if strings.ContainsRune(card, rune(FIVE)) {
		rank = Rank(FIVE)
	} else if strings.ContainsRune(card, rune(SIX)) {
		rank = Rank(SIX)
	} else if strings.ContainsRune(card, rune(SEVEN)) {
		rank = Rank(SEVEN)
	} else if strings.ContainsRune(card, rune(EIGHT)) {
		rank = Rank(EIGHT)
	} else if strings.ContainsRune(card, rune(NINE)) {
		rank = Rank(NINE)
	} else if strings.ContainsRune(card, rune(TEN)) {
		rank = Rank(TEN)
	} else if strings.ContainsRune(card, rune(JACK)) {
		rank = Rank(JACK)
	} else if strings.ContainsRune(card, rune(QUEEN)) {
		rank = Rank(QUEEN)
	} else if strings.ContainsRune(card, rune(KING)) {
		rank = Rank(KING)
	} else if strings.ContainsRune(card, rune(ACE)) {
		rank = Rank(ACE)
	} else {
		return Card{}, fmt.Errorf("Invalid card string %s", card)
	}

	if rank == Rank(0) || suit == Suit(0) {
		return Card{}, fmt.Errorf("Invalid card string %s", card)
	}
	return Card{Rank: rank, Suit: suit}, nil
}

func NewCardsFromString(cards string) (Cards, error) {
	// supports a string like 9♠,9♣,Q♥,Q♦,K♣
	// FIXME extend this later to common format strings like "9c Qh Qd Kc"
	// with or without commas
	var newCards Cards
	newCards = make(Cards, 0)
	cardStrings := strings.Split(cards, ",")
	for _, cardString := range cardStrings {
		card, err := NewCardFromString(cardString)
		if err != nil {
			return Cards{}, err
		}
		newCards = append(newCards, card)
	}
	if len(newCards) == 0 {
		return Cards{}, fmt.Errorf("No cards found in string %s", cards)
	}
	return newCards, nil
}

func (c *Card) String() string {
	return fmt.Sprintf("%s%s", string(c.Rank), string(c.Suit))
}

type Cards []Card

func (c Cards) First() Card {
	return c[0]
}

func (c Cards) Second() Card {
	return c[1]
}

func (c Cards) Third() Card {
	return c[2]
}

func (c Cards) Fourth() Card {
	return c[3]
}

func (c Cards) Fifth() Card {
	return c[4]
}

func (c Cards) Last() Card {
	return c[len(c)-1]
}

func (c Cards) SortByRankAscending() Cards {
	newCards := make(Cards, len(c))
	copy(newCards, c)
	sort.Slice(newCards, func(i, j int) bool {
		return newCards[i].Rank.Score() < newCards[j].Rank.Score()
	})

	return newCards
}

func (c Cards) PrintToTerminal() {
	fmt.Printf("%s", c.FormatForTerminal())
}

type SortOrder int

const (
	AceHighAscending SortOrder = iota
	AceHighDescending
)

func (c Cards) FormatForTerminalSorted(order SortOrder) string {
	sorted := c.SortByRankAscending() // this is ascending
	if order == AceHighDescending {
		slices.Reverse(sorted)
	}
	return sorted.FormatForTerminal()
}

func (c Cards) FormatForTerminal() string {
	var cardstrings []string
	for i := 0; i < len(c); i++ {
		cardstrings = append(cardstrings, c[i].FormatForTerminal())
	}
	return strings.Join(cardstrings, ",")
}

func (c Card) FormatForTerminal() string {
	var rank string
	var suit string
	color := aurora.Red
	switch c.Suit {
	case Suit(DIAMOND):
		color = aurora.Blue
	case Suit(HEART):
		color = aurora.Red
	case Suit(CLUB):
		color = aurora.Green
	case Suit(SPADE):
		color = aurora.Black
	}

	rank = fmt.Sprintf("%s", aurora.Bold(color(c.Rank.Symbol())))
	suit = fmt.Sprintf("%s", aurora.Bold(color(c.Suit.Symbol())))
	return fmt.Sprintf("%s%s", rank, suit)
}

func (c Cards) HighestRank() Rank {
	sorted := c.SortByRankAscending()
	return sorted[len(sorted)-1].Rank
}

func (s Cards) String() (output string) {
	var cardstrings []string
	for i := 0; i < len(s); i++ {
		cardstrings = append(cardstrings, s[i].String())
	}
	output = strings.Join(cardstrings, ",")
	return output
}
