from __future__ import annotations

from dataclasses import dataclass, asdict
from typing import Any
import re
import unicodedata

QUALITY_NAMES = ["Zwykły", "Dobry", "Doskonały", "Epicki"]
QUALITY_ALIASES = {
    "zwykly": 0, "zwykla": 0, "zwykle": 0,
    "dobry": 1, "dobra": 1, "dobre": 1,
    "doskonaly": 2, "doskonala": 2, "doskonale": 2,
    "epicki": 3, "epicka": 3, "epickie": 3,
}
MAX_LEVEL = 3 * 6 + 5  # Epicki +5. Ceiling is capped until verified otherwise.
MIN_COMBINE_LEVEL = 1  # Zwykły +1


def norm(value: Any) -> str:
    if value is None:
        return ""
    text = str(value).translate(str.maketrans({"ł": "l", "Ł": "L"}))
    text = unicodedata.normalize("NFKD", text)
    text = "".join(ch for ch in text if not unicodedata.combining(ch))
    text = text.lower().strip()
    return re.sub(r"\s+", " ", text)


def quality_rank(value: str | None) -> int:
    return QUALITY_ALIASES.get(norm(value), 0)


def parse_upgrade(value: str | int | None) -> int:
    if value is None or value == "":
        return 0
    if isinstance(value, int):
        return max(0, min(5, value))
    m = re.search(r"(\d+)", str(value))
    return max(0, min(5, int(m.group(1)))) if m else 0


def quality_level(quality: str | None, upgrade: str | int | None) -> int:
    return quality_rank(quality) * 6 + parse_upgrade(upgrade)


def level_parts(level: int) -> tuple[int, int]:
    level = max(0, min(MAX_LEVEL, int(level)))
    return level // 6, level % 6


def level_label(level: int) -> str:
    rank, upgrade = level_parts(level)
    return f"{QUALITY_NAMES[rank]}" + (f" +{upgrade}" if upgrade else "")


@dataclass(frozen=True)
class Identity:
    category: str
    base: str
    prefix: str = ""
    suffix: str = ""
    legendary: bool = False

    def key(self) -> str:
        return "|".join([
            self.category,
            self.base or "",
            self.prefix or "",
            self.suffix or "",
            "1" if self.legendary else "0",
        ])

    def same_item(self, other: "Identity") -> bool:
        return self == other


@dataclass
class InventoryItem:
    db_id: int | None
    profile_id: int | None
    import_id: int | None
    item_id: str
    full_name: str
    identity: Identity
    quality: str
    upgrade: int
    level: int
    shelf: str = ""
    html_type: str = ""
    parser_notes: str = ""
    raw_prefix: str = ""
    raw_base: str = ""
    raw_suffix: str = ""
    main_image: str = ""
    prefix_image: str = ""
    suffix_image: str = ""

    def to_dict(self) -> dict[str, Any]:
        data = asdict(self)
        data["identity"] = asdict(self.identity)
        return data


@dataclass
class CraftStep:
    left: Identity
    right: Identity
    result: Identity
    left_source: str
    right_source: str
    result_level: int


@dataclass
class Plan:
    target: Identity
    steps: list[CraftStep]
    consumed_item_ids: list[int]
    missing: list[Identity]
    result_level: int
    notes: list[str]
    # Longest dependency branch in the plan.  This is different from the total
    # number of crafting operations: A+C->X, B+D->Z, X+Z->E has 3 operations,
    # but only 2 layers of depth.
    craft_depth: int = 0

    @property
    def missing_count(self) -> int:
        return len(self.missing)

    @property
    def step_count(self) -> int:
        return len(self.steps)
