package parser

import (
	"autobrew/action"
	"autobrew/ingredient"
	"autobrew/recipe"
	"autobrew/utils"
	"bufio"
	"io/fs"
	"log"
	"regexp"
	"strconv"
	"strings"
)

var add_to_cauldron = regexp.MustCompile(`(?mi)^Add (\d+ .+)+? to the cauldron\.$`)
var add_to_mortar = regexp.MustCompile(`(?mi)^Add (\d+ .+)+? to the mortar, grind, and pour into the cauldron\.$`)
var boil = regexp.MustCompile(`(?mi)^Boil for (\d+) turn[s]*\.$`)
var boil_bellows = regexp.MustCompile(`(?mi)^Boil for (\d+) turn[s]* using bellows\.$`)
var extract_amount = regexp.MustCompile(`(?mi)^(\d+) (.*)$`)

func transformStep(lines []string) (name string, steps []recipe.Step, err error) {

	name, lines = lines[0], lines[1:]

	var ingredients []ingredient.Ingredient

	for index, line := range lines {
		if strings.HasPrefix(line, "Liquid Base:") {
			i, err := ingredient.IngredientFromString(strings.TrimSpace(strings.Split(line, ": ")[1]))
			utils.Check(err)

			ingredients = append(ingredients, i)
		}

		if strings.HasPrefix(line, "Ingredients:") {
			parts := strings.TrimSpace(strings.Split(line, ":")[1])
			for _, i := range strings.Split(parts, ",") {

				a := strings.Split(strings.TrimSpace(i), " x")
				i, err := ingredient.IngredientFromString(strings.TrimSpace(a[0]))
				utils.Check(err)

				amount, err := strconv.Atoi(a[1])
				utils.Check(err)

				for range amount {
					ingredients = append(ingredients, i)
				}
			}
		}

		if strings.HasPrefix(line, "Recipe") {
			lines = lines[index+1:]
		}
	}

	steps = append(steps, recipe.Step{
		Action:      action.Prepare,
		Ingredients: ingredients,
	})

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if match := add_to_cauldron.FindStringSubmatch(line); match != nil {
			parts := strings.Split(match[1], " and ")
			var ingredients []ingredient.Ingredient
			for _, part := range parts {
				if match := extract_amount.FindStringSubmatch(part); match != nil {
					i, err := strconv.Atoi(match[1])
					utils.Check(err)

					for range i {
						i, err := ingredient.IngredientFromString(strings.TrimSpace(match[2]))
						utils.Check(err)

						ingredients = append(ingredients, i)
					}

				}
			}
			steps = append(steps, recipe.Step{
				Action:      action.AddToCauldron,
				Ingredients: ingredients,
			})
		}

		if match := add_to_mortar.FindStringSubmatch(line); match != nil {
			parts := strings.Split(match[1], " and ")
			var ingredients []ingredient.Ingredient
			for _, part := range parts {
				if match := extract_amount.FindStringSubmatch(part); match != nil {
					i, err := strconv.Atoi(match[1])
					utils.Check(err)

					for range i {
						i, err := ingredient.IngredientFromString(strings.TrimSpace(match[2]))
						utils.Check(err)

						ingredients = append(ingredients, i)
					}

				}
			}
			steps = append(steps, recipe.Step{
				Action:      action.GrindIngredients,
				Ingredients: ingredients,
			})
		}

		if match := boil.FindStringSubmatch(line); match != nil {
			i, err := strconv.Atoi(match[1])
			utils.Check(err)
			steps = append(steps, recipe.Step{
				Action: action.Boil,
				Turns:  i,
			})
		}

		if match := boil_bellows.FindStringSubmatch(line); match != nil {
			i, err := strconv.Atoi(match[1])
			utils.Check(err)
			steps = append(steps, recipe.Step{
				Action: action.BoilBellows,
				Turns:  i,
			})
		}

		if strings.HasPrefix(line, "Pour into phial.") {
			steps = append(steps, recipe.Step{
				Action: action.PourPhial,
			})
		}

		if strings.HasPrefix(line, "Prepare phial and distill.") {
			steps = append(steps, recipe.Step{
				Action: action.Distill,
			})
		}

		if strings.HasPrefix(line, "Pour into a mortar and grind.") {
			steps = append(steps, recipe.Step{
				Action: action.GrindPotion,
			})
		}
	}

	return
}

func FromText(file fs.File) map[string][]recipe.Step {
	scanner := bufio.NewScanner(file)
	// optionally, resize scanner's capacity for lines over 64K, see next example
	recipes := make(map[string][]recipe.Step, 0)
	lines := make([]string, 0)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if len(line) == 0 {
			continue
		}

		if line == "--" {
			name, recipe, err := transformStep(lines)
			utils.Check(err)
			recipes[name] = recipe
			lines = make([]string, 0)
			continue
		}

		lines = append(lines, line)

	}

	if len(lines) > 0 {
		name, recipe, err := transformStep(lines)
		utils.Check(err)
		recipes[name] = recipe
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}

	return recipes
}
