package main

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var validFields = []string{
	"byr", // Birth Year
	"iyr", // Issue Year
	"eyr", // Expiration Year
	"hgt", // Height
	"hcl", // Hair Color
	"ecl", // Eye Color
	"pid", // Passport ID
	"cid", // Country ID
}

var requiredFields = []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"}
var validEyeColors = []string{"amb", "blu", "brn", "gry", "grn", "hzl", "oth"}

// Passport ...
type Passport map[string]string

func stringInSlice(slice []string, str string) bool {
	for _, s := range slice {
		if s == str {
			return true
		}
	}
	return false
}

// HasAllFields checks that all requried fields are present.
func (p Passport) HasAllFields() bool {
	for _, s := range requiredFields {
		if _, ok := p[s]; !ok {
			return false
		}
	}
	return true
}

// IsValid validates a passport.
func (p Passport) IsValid() bool {
	for k, v := range p {
		switch k {
		case "byr":
			val, _ := strconv.Atoi(v)
			if !(val >= 1920 && val <= 2002) {
				return false
			}
		case "iyr":
			val, _ := strconv.Atoi(v)
			if !(val >= 2010 && val <= 2020) {
				return false
			}
		case "eyr":
			val, _ := strconv.Atoi(v)
			if !(val >= 2020 && val <= 2030) {
				return false
			}
		case "hgt":
			if len(v) < 3 {
				return false
			}

			unit := v[len(v)-2:]
			measurement, _ := strconv.Atoi(v[:len(v)-2])
			switch unit {
			case "cm":
				if !(measurement >= 150 && measurement <= 193) {
					return false
				}
			case "in":
				if !(measurement >= 59 && measurement <= 76) {
					return false
				}
			default:
				return false
			}
		case "hcl":
			if len(v) != 7 {
				return false
			}
			matched, _ := regexp.MatchString("[a-fA-F0-9]+", v[1:])
			if !matched {
				return false
			}
		case "ecl":
			if !stringInSlice(validEyeColors, v) {
				return false
			}
		case "pid":
			if len(v) != 9 {
				return false
			}
			matched, _ := regexp.MatchString(`^\d+$`, v[1:])
			if !matched {
				return false
			}
		default:
		}
	}

	return true
}

func (p Passport) String() string {
	var result strings.Builder

	fmt.Fprintf(
		&result, `╭─────────────────────────────────────────╮
│ Passport Card                           │
│ +-------+ Issuing Country   Passport ID │
│ |  /-\  |  %03s               %09s  │
│ |  \-/  |                               │
│ | /-u-\ | Issued            Expires     │
│ +-------+  %04s              %04s       │
│                                         │
│ Height       Hair Color       Eye Color │
│  %05s        %07s          %03s      │
│ Birth Year                              │
│  %04s                                   │
╰─────────────────────────────────────────╯`, p["cid"], p["pid"],
		p["iyr"], p["eyr"], p["hgt"], p["hcl"], p["ecl"], p["byr"],
	)

	return result.String()
}

// Task1 ...
func Task1(input []Passport) int {
	var result int
	for _, i := range input {
		if i.HasAllFields() {
			result++
		}
	}
	return result
}

// Task2 ...
func Task2(input []Passport) int {
	var result int
	for _, i := range input {
		if i.HasAllFields() && i.IsValid() {
			result++
		}
	}
	return result
}

func main() {
	var input []Passport
	file, _ := os.Open("input.txt")
	scanner := bufio.NewScanner(file)

	p := Passport{}
	for scanner.Scan() {
		data := strings.Split(scanner.Text(), " ")
		if len(data) == 1 && data[0] == "" {
			input = append(input, p)
			p = Passport{}
			continue
		}

		for _, d := range data {
			kv := strings.Split(d, ":")
			if stringInSlice(validFields, kv[0]) {
				p[kv[0]] = kv[1]
			}
		}
	}
	input = append(input, p)
	file.Close()

	result := Task1(input)
	fmt.Printf("Task 1: %d\n", result)

	result = Task2(input)
	fmt.Printf("Task 2: %d\n", result)
}