"""Deck list parsing and serialization utilities."""
import re
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass


@dataclass
class CardInfo:
    """Card information extracted from deck text."""
    count: int
    name: str
    set_code: Optional[str] = None
    collector_number: Optional[str] = None
    
    def __str__(self) -> str:
        if self.set_code and self.collector_number:
            return f"{self.count} {self.name} ({self.set_code}-{self.collector_number})"
        return f"{self.count} {self.name}"


class DeckParser:
    """Parse MTG Online deck list formats."""
    
    # Regex patterns for deck parsing
    CARD_LINE_RE = re.compile(r"^\s*[\w\[\(\{].*$")
    EMPTY_LINE_RE = re.compile(r"^\s*$")
    COMMENT_RE = re.compile(r"([\w\[\(\{].*$)")
    SB_MARK_RE = re.compile(r"^\s*sb:\s*(.+)", re.IGNORECASE)
    SB_COMMENT_RE = re.compile(r"^sideboard\s.*$", re.IGNORECASE)
    DECK_COMMENT_RE = re.compile(r"^((main)?deck(list)?|mainboard)\b", re.IGNORECASE)
    MULTIPLIER_RE = re.compile(r"^([xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))")
    HYPHEN_FORMAT_RE = re.compile(r"\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*)")
    REGULAR_FORMAT_RE = re.compile(r"\((\w{3,})\)\s+(\d+[^\w\s]*)")
    
    def parse_plain_text(self, text: str) -> List[CardInfo]:
        """Parse plain text deck format."""
        lines = text.strip().split('\n')
        max_line = len(lines)
        
        # Find deck start (first card line)
        deck_start = -1
        for i, line in enumerate(lines):
            if self.CARD_LINE_RE.match(line):
                deck_start = i
                break
        
        if deck_start == -1:
            return []
        
        # Find sideboard start
        sBStart = -1
        for i in range(deck_start, max_line):
            if self.SB_MARK_RE.match(lines[i]):
                sBStart = i
                break
            elif self.SB_COMMENT_RE.match(lines[i]):
                sBStart = i
                break
        
        if sBStart == -1:
            # Look for empty line after deck
            for i in range(deck_start + 1, max_line):
                if self.EMPTY_LINE_RE.match(lines[i]):
                    # Check if there are cards after the empty line
                    for j in range(i + 1, max_line):
                        if self.CARD_LINE_RE.match(lines[j]):
                            sBStart = i
                            break
                    if sBStart != -1:
                        break
        
        if sBStart == -1:
            sBStart = max_line
        
        cards = []
        index = 0
        
        # Skip comments
        while index < deck_start:
            if not self.EMPTY_LINE_RE.match(lines[index]):
                index += 1
            else:
                break
        
        # Parse cards
        for i in range(index, sBStart):
            line = lines[i].strip()
            if not line or self.EMPTY_LINE_RE.match(line):
                continue
            
            # Check for sideboard marker
            if self.SB_MARK_RE.match(line):
                match = self.SB_MARK_RE.match(line)
                card_name = match.group(1).strip() if match else line
                sideboard = True
            else:
                card_name = line
                sideboard = False
            
            # Extract set code and collector number
            set_code = None
            collector_number = None
            match = self.HYPHEN_FORMAT_RE.search(card_name)
            if match:
                set_code = match.group(2).upper()
                collector_number = match.group(3)
                card_name = card_name[:match.start()].strip()
            else:
                match = self.REGULAR_FORMAT_RE.search(card_name)
                if match:
                    set_code = match.group(1).upper()
                    collector_number = match.group(2)
                    card_name = card_name[:match.start()].strip()
            
            # Extract count
            count = 1
            match = self.MULTIPLIER_RE.match(card_name)
            if match:
                count = int(match.group(2))
                card_name = match.group(3)
            
            # Normalize card name
            card_name = card_name.strip()
            
            if card_name:
                cards.append(CardInfo(
                    count=count,
                    name=card_name,
                    set_code=set_code,
                    collector_number=collector_number,
                ))
        
        return cards
    
    def format_plain_text(self, cards: List[CardInfo], prefix_sideboard: bool = True) -> str:
        """Format cards as plain text deck list."""
        lines = []
        for card in cards:
            prefix = "SB: " if prefix_sideboard and card.name.startswith("[SB]") else ""
            name = card.name
            if card.name.startswith("[SB]"):
                name = card.name[4:]  # Remove [SB] prefix
            
            if card.set_code and card.collector_number:
                lines.append(f"{prefix}{card.count} {name} ({card.set_code}-{card.collector_number})")
            else:
                lines.append(f"{prefix}{card.count} {name}")
        
        return "\n".join(lines)
    
    def to_native_xml(self, cards: List[CardInfo]) -> str:
        """Convert cards to MTG Online native XML format."""
        xml_lines = ['<mtgonline_deck version="1">']
        
        # Group cards by zone (main/sideboard)
        main_cards = [c for c in cards if not c.name.startswith("[SB]")]
        side_cards = [c for c in cards if c.name.startswith("[SB]")]
        
        if main_cards:
            xml_lines.append('  <zone name="main">')
            for card in main_cards:
                xml_lines.append(f'    <card name="{card.name}" count="{card.count}" />')
            xml_lines.append('  </zone>')
        
        if side_cards:
            xml_lines.append('  <zone name="sideboard">')
            for card in side_cards:
                clean_name = card.name[4:]  # Remove [SB] prefix
                xml_lines.append(f'    <card name="{clean_name}" count="{card.count}" />')
            xml_lines.append('  </zone>')
        
        xml_lines.append('</mtgonline_deck>')
        return "\n".join(xml_lines)
    
    def from_native_xml(self, xml: str) -> List[CardInfo]:
        """Parse MTG Online native XML format."""
        cards = []
        
        # Simple XML parsing (in production, use proper XML parser)
        card_pattern = re.compile(r'<card\s+name="([^"]+)"\s+count="(\d+)"\s*/>')
        
        for match in card_pattern.finditer(xml):
            name = match.group(1)
            count = int(match.group(2))
            cards.append(CardInfo(count=count, name=name))
        
        return cards


# Convenience functions
def parse_deck(text: str) -> List[CardInfo]:
    """Parse a deck list from plain text or native XML."""
    parser = DeckParser()
    if text.strip().startswith("<mtgonline_deck"):
        return parser.from_native_xml(text)
    else:
        return parser.parse_plain_text(text)


def format_deck(cards: List[CardInfo], format_type: str = "plain", prefix_sideboard: bool = True) -> str:
    """Format cards to specified deck format."""
    parser = DeckParser()
    if format_type == "native":
        return parser.to_native_xml(cards)
    else:
        return parser.format_plain_text(cards, prefix_sideboard)
