package day19

import (
	"strings"
)

func Part1(input string) int {
	towelPatterns, designs := parseInput(input)
	result := countPossibleDesigns(towelPatterns, designs)
	return result
}

func Part2(input string) int {
	towelPatterns, designs := parseInput(input)
	result := totalArrangements(towelPatterns, designs)
	return result
}

func parseInput(input string) ([]string, []string) {
	parts := strings.SplitN(input, "\n\n", 2)
	towels := strings.Split(strings.ReplaceAll(parts[0], " ", ""), ",")
	patterns := strings.Split(strings.TrimSpace(parts[1]), "\n")
	return towels, patterns
}

func countPossibleDesigns(towelPatterns []string, designs []string) int {
	memo := make(map[string]bool)
	count := 0

	for _, design := range designs {
		if canConstruct(design, towelPatterns, memo) {
			count++
		}
	}

	return count
}

func canConstruct(design string, patterns []string, memo map[string]bool) bool {
	if design == "" {
		return true
	}
	if val, found := memo[design]; found {
		return val
	}

	for _, pattern := range patterns {
		if strings.HasPrefix(design, pattern) {
			remaining := design[len(pattern):]
			if canConstruct(remaining, patterns, memo) {
				memo[design] = true
				return true
			}
		}
	}

	memo[design] = false
	return false
}

func totalArrangements(towelPatterns []string, designs []string) int {
	memo := make(map[string]int)
	total := 0

	for _, design := range designs {
		total += countWays(design, towelPatterns, memo)
	}

	return total
}

func countWays(design string, patterns []string, memo map[string]int) int {
	if design == "" {
		return 1
	}
	if val, found := memo[design]; found {
		return val
	}

	totalWays := 0
	for _, pattern := range patterns {
		if strings.HasPrefix(design, pattern) {
			remaining := design[len(pattern):]
			totalWays += countWays(remaining, patterns, memo)
		}
	}

	memo[design] = totalWays
	return totalWays
}
