from __future__ import annotations

from dataclasses import dataclass
from itertools import combinations
from collections import defaultdict

from models import Identity, InventoryItem, MIN_COMBINE_LEVEL, MAX_LEVEL, level_label
from rules import RuleBook


@dataclass
class CraftOption:
    left: InventoryItem
    right: InventoryItem
    result: Identity
    result_level: int
    notes: list[str]

    @property
    def result_level_label(self) -> str:
        return level_label(self.result_level)


def combine_levels(left_identity: Identity, left_level: int, right_identity: Identity, right_level: int) -> int:
    bonus = 1 if left_identity.same_item(right_identity) else 0
    return min(MAX_LEVEL, min(left_level, right_level) + bonus)


def combine_items(left: InventoryItem, right: InventoryItem, rules: RuleBook) -> CraftOption | None:
    if left.identity.category != right.identity.category:
        return None
    if left.identity.legendary != right.identity.legendary:
        return None
    if left.level < MIN_COMBINE_LEVEL or right.level < MIN_COMBINE_LEVEL:
        return None
    result, notes = rules.combine_identity(left.identity, right.identity)
    if result is None:
        return None
    result_level = combine_levels(left.identity, left.level, right.identity, right.level)
    return CraftOption(left, right, result, result_level, notes)


def all_direct_crafts(items: list[InventoryItem], rules: RuleBook) -> list[CraftOption]:
    out: list[CraftOption] = []
    for left, right in combinations(items, 2):
        option = combine_items(left, right, rules)
        if option:
            out.append(option)
    out.sort(key=lambda x: (
        x.result.category,
        0 if x.result.legendary else 1,
        rules.identity_display(x.result),
        -x.result_level,
        x.left.full_name,
        x.right.full_name,
    ))
    return out


def grouped_direct_crafts(options: list[CraftOption]) -> list[dict]:
    grouped: dict[tuple[str, int], list[CraftOption]] = defaultdict(list)
    for option in options:
        grouped[(option.result.key(), option.result_level)].append(option)
    rows = []
    for (_key, level), group in grouped.items():
        sample = group[0]
        rows.append({
            "result": sample.result,
            "result_level": level,
            "pair_count": len(group),
            "sample_left": sample.left.full_name,
            "sample_right": sample.right.full_name,
        })
    return rows
