"""
Advent of Code 2021 - Day 10

Run with:
    python puzzles/day10.py inputs/day10.txt
"""

import pathlib
import sys
from typing import Dict, List, Tuple

from statistics import median


class Subsystem:
    nav_data = None
    open_chars = ["(", "[", "{", "<"]
    close_chars = [")", "]", "}", ">"]

    def __init__(self, inputs: List[str]) -> None:
        self.nav_data = inputs
        self.validate()

    def validate(self) -> None:
        chunks = {"corrupted": [], "incomplete": [], "complete": []}
        corrupted_chars = []
        incomplete_chunks = []

        for line in self.nav_data:
            corrupted = False

            opens = []
            for char in line:
                if char in self.open_chars:
                    opens.append(char)
                    continue

                open_char = self.open_chars[self.close_chars.index(char)]
                if opens[-1] != open_char:
                    corrupted = True
                    corrupted_chars.append(char)
                    break

                del opens[-1]  # Clear the open bracket

            if corrupted:
                chunks["corrupted"].append(line)
            elif len(opens) != 0:
                chunks["incomplete"].append(line)
                incomplete_chunks.append(opens)
            else:
                chunks["complete"].append(line)

        self.chunks = chunks
        self.corrupted_chars = corrupted_chars
        self.incomplete_chunks = incomplete_chunks

    def score_corrupted(self) -> int:
        scores = {")": 3, "]": 57, "}": 1197, ">": 25137,}
        return sum([scores[char] for char in self.corrupted_chars])

    def score_incomplete(self) -> int:
        scores = {")": 1, "]": 2, "}": 3, ">": 4,}
        total_scores = []
        for chunk in self.incomplete_chunks:
            total_score = 0
            for char in reversed(chunk):
                close_char = self.close_chars[self.open_chars.index(char)]
                total_score = total_score * 5 + scores[close_char]
            total_scores.append(total_score)

        return median(total_scores)


def part1(inputs: List[int]) -> int:
    s = Subsystem(inputs)
    return s.score_corrupted()


def part2(inputs: List[int]) -> int:
    s = Subsystem(inputs)
    return s.score_incomplete()


def parse(inputs: str) -> List[int]:
    """Parse the input string"""
    return inputs.split()


def solve(path: str) -> Tuple[int, int]:
    """Solve the puzzle"""
    puzzle_input = parse(pathlib.Path(path).read_text().strip())
    part1_result = part1(puzzle_input)
    part2_result = part2(puzzle_input)

    return part1_result, part2_result


def main() -> None:
    for path in sys.argv[1:]:
        print(f"Input File: {path}")

        part1_result, part2_result = solve(path)

        print(f"Part 1 Result: {part1_result}")
        print(f"Part 2 Result: {part2_result}")


if __name__ == "__main__":
    main()