package day07

import (
	_ "fmt"
	"strings"
	"sort"
	"adventofcode2023/utils"

)
type Card int

const (
	_ Card = 1 + iota
    Two
    Three 
    Four
	Five
	Six
	Seven
	Eight
	Nine
	Ten 
	Jack
	Queen
	King
	Ace
)

type Card2 int
const (
	_ Card2 = iota
	Joker
    Two2
    Three2 
    Four2
	Five2
	Six2
	Seven2
	Eight2
	Nine2
	Ten2 
	Queen2
	King2
	Ace2
)

type HandType int

const (
	_ HandType = iota
	HighCard
	OnePair
	TwoPair
	ThreeKind
	FullHouse
	FourKind
	FiveKind
)

type Hand struct {
	cards [5]Card
	bid int
	rank int
	handType HandType
}

type Hand2 struct {
	cards [5]Card2
	bid int
	rank int
	handType HandType
}

func Part1(input string) int {
	total := 0
	hands := parseInput(1, input)
	sort.Slice(hands, func(i, j int) bool {
		if hands[i].handType == hands[j].handType {
			for k:=0;k<5;k++{
				if hands[i].cards[k] == hands[j].cards[k] {
					continue
				}
				return hands[i].cards[k] < hands[j].cards[k]
			}
		} 
		return hands[i].handType < hands[j].handType
	})
	for i,hand := range hands {
		total += (i+1) * hand.bid
	}
	return total
}

func Part2(input string) int {
	total := 0
	hands := parseInput(2, input)
	sort.Slice(hands, func(i, j int) bool {
		if hands[i].handType == hands[j].handType {
			for k:=0;k<5;k++{
				if hands[i].cards[k] == hands[j].cards[k] {
					continue
				}
				if hands[i].cards[k] == Jack {return true}
				if hands[j].cards[k] == Jack {return false}
				return hands[i].cards[k] < hands[j].cards[k]
			}
		} 
		return hands[i].handType < hands[j].handType
	})
	for i,hand := range hands {
		total += (i+1) * hand.bid
	}
	return total
}

func parseInput(part int, input string) ([]Hand) {
	var hand Hand
	var hands []Hand
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		tokens := strings.Fields(line)
		hand.bid = utils.MustAtoi(tokens[1])
		for i, j := range tokens[0] {
				hand.cards[i]	= toCard(j)
		}
		hand.handType = toHandType(part, hand.cards)
		hands = append(hands, hand)
	}
	return hands
}

func toCard(j rune) Card {
	switch j {
	case '2':
		return Two
	case '3':
		return Three
	case '4':
		return Four
	case '5':
		return Five
	case '6':
		return Six
	case '7':
		return Seven
	case '8':
		return Eight
	case '9':
		return Nine
	case 'T':
		return Ten
	case 'J':
		return Jack
	case 'Q':
		return Queen
	case 'K':
		return King
	case 'A':
		return Ace
	}
	return -1
}

func toHandType(part int, cards [5]Card) HandType {
	jokers := 0
	handType := HighCard
	cardMap := make(map[Card]int)
	for _, card := range cards {
		if part == 2 && card == Jack {
			jokers++
		} else {
			if _, ok := cardMap[card]; ok {
				cardMap[card]++
			} else {
				cardMap[card] = 1
			}
		}
	}
	cardSorted := ToSortedSlice(cardMap)
	if part == 2 && jokers == 5 { return FiveKind}
	if part == 2 && jokers > 0 {
		cardSorted[0].Value = cardSorted[0].Value + jokers
	}
	if cardSorted[0].Value == 5 { handType = FiveKind}
	if cardSorted[0].Value == 4 { handType = FourKind}
	if cardSorted[0].Value == 3 {
		if cardSorted[1].Value == 2 {handType = FullHouse}
		if cardSorted[1].Value == 1 {handType = ThreeKind}
	}
	if cardSorted[0].Value == 2 {
		if cardSorted[1].Value == 2 {handType = TwoPair}
		if cardSorted[1].Value == 1 {handType = OnePair}	
	}
	return handType
}

//func toHandType(part int, cards [5]Card) HandType {
//	cardMap := make(map[Card]int)
//	for _, card := range cards {
//		if _, ok := cardMap[card]; ok {
//			cardMap[card]++
//		} else {
//			cardMap[card] = 1
//		}
//	}
//	if part == 2 {
//		switch cardMap[Jack] {
//		case 5:
//			return FiveKind
//		case 4:
//			return FiveKind
//		case 3:
//			if len(cardMap) == 2 {return FiveKind}
//			return FourKind
//		case 2:
//			switch len(cardMap) {
//			case 2:
//				return FiveKind
//			case 3:
//				return FourKind
//			case 4:
//				return ThreeKind
//
//			}
//		case 1:
//			switch len(cardMap) {
//			case 2:
//				return FiveKind
//			case 3:
//				max := utils.MapFindMax(cardMap)
//				if max == 3 {return FourKind}
//				if max == 2 {return FullHouse}
//			case 4:
//				return ThreeKind
//			case 5:
//				return OnePair
//
//			}			
//		}
//	}
//	switch len(cardMap) {
//	case 5:
//		return HighCard
//	case 4:
//		return OnePair
//	case 3:
//		for _, count := range cardMap {
//			if count == 3 {
//				return ThreeKind
//			}
//		}
//		return TwoPair
//	case 2:
//		for _, count := range cardMap {
//			if count == 4 {
//				return FourKind
//			} 
//		}
//		return FullHouse
//	case 1:
//		return FiveKind
//	}
//	return -1
//}

// KeyValue is a struct to hold key-value pairs
type KeyValue struct {
	Key   Card
	Value int
}

func ToSortedSlice(myMap map[Card]int) []KeyValue {

	var keyValuePairs []KeyValue
	for key, value := range myMap {
		keyValuePairs = append(keyValuePairs, KeyValue{key, value})
	}

	// Sort the slice by values
	sort.Slice(keyValuePairs, func(i, j int) bool {
		if keyValuePairs[i].Value == keyValuePairs[j].Value { return keyValuePairs[i].Key > keyValuePairs[j].Key }
		return keyValuePairs[i].Value > keyValuePairs[j].Value
	})

	// Create a new map with sorted key-value pairs
	//sortedMap := make(map[Card]int)
	//for _, kv := range keyValuePairs {
	//	sortedMap[kv.Key] = kv.Value
	//}
	return keyValuePairs
}