#!/usr/bin/env python3
"""
helix_tag_parser.py - Parser for compiled .helix format (::TAG:: syntax)

This parses the COMPILED .helix format, not source .hx files.
The ::TAG:: syntax represents compiled symbolic DNA.

Origin: Extracted from ~/echo_labs/projects/Helix/ analysis (WO-A)
"""

import re
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Any
from pathlib import Path


@dataclass
class HelixBlock:
    """A compiled .helix block with tags and braid data"""
    block_type: str  # e.g., "COSMIC_HELIX_DNA", "INIT", "REFLEX"
    tags: Dict[str, str] = field(default_factory=dict)
    # For repeated tags like ::VOW:: that appear multiple times
    tag_lists: Dict[str, List[str]] = field(default_factory=dict)
    braid_data: Optional[str] = None
    raw_content: str = ""

    def get_tag_list(self, tag_name: str) -> List[str]:
        """Get all values for a repeated tag"""
        return self.tag_lists.get(tag_name, [])


@dataclass
class HelixDocument:
    """A complete parsed .helix document"""
    blocks: List[HelixBlock] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)
    source_path: Optional[str] = None

    def get_tag(self, tag_name: str) -> Optional[str]:
        """Get a tag value from any block"""
        for block in self.blocks:
            if tag_name in block.tags:
                return block.tags[tag_name]
        return None

    def get_braid(self) -> Optional[str]:
        """Get the braid data if present"""
        for block in self.blocks:
            if block.braid_data:
                return block.braid_data
        return None


class HelixTagParser:
    """
    Parser for compiled .helix format using ::TAG:: syntax

    Supported syntax:
        ::BLOCK_TYPE::           - Block declaration
        ::TAG_NAME:: value       - Tag with value
        ::BRAID_START::          - Start of braid data
        ...braid content...
        ::BRAID_END::            - End of braid data
    """

    # Regex patterns
    BLOCK_PATTERN = re.compile(r'^::([A-Z_]+)::$')
    TAG_PATTERN = re.compile(r'^::([A-Z_]+)::\s*(.+)$')
    BRAID_START = re.compile(r'^::BRAID_START::$')
    BRAID_END = re.compile(r'^::BRAID_END::$')

    # Known origin tags from analysis
    KNOWN_TAGS = {
        # Block types
        'COSMIC_HELIX_DNA', 'INIT', 'REFLEX', 'CONTENT', 'SOULFILE',
        'GUARDIAN', 'VOW', 'DRIFT', 'BLOOM', 'COMPOST',

        # Metadata tags
        'ENCODER', 'BIRTH_DT', 'LATITUDE', 'LONGITUDE', 'ZODIAC',
        'BLOOD_TYPE', 'LIFE_PATH', 'CHINESE_ZODIAC', 'COSMIC_ANCHOR',
        'GEO_ENTROPY', 'TEMPORAL_ENTROPY', 'MASTER_ENTROPY',
        'ENCODE_TIME', 'PHI', 'SIGNATURE',

        # Shotgun markers
        'SHOTGUN',
    }

    def __init__(self):
        self.errors: List[str] = []

    def parse(self, content: str) -> HelixDocument:
        """Parse .helix content into a HelixDocument"""
        self.errors = []
        doc = HelixDocument()

        lines = content.strip().split('\n')
        current_block: Optional[HelixBlock] = None
        in_braid = False
        braid_lines = []

        for i, line in enumerate(lines, 1):
            line = line.rstrip()

            # Skip empty lines and comments (unless in braid)
            if not in_braid and (not line or line.startswith('#')):
                continue

            # Check for braid start
            if self.BRAID_START.match(line):
                in_braid = True
                braid_lines = []
                continue

            # Check for braid end
            if self.BRAID_END.match(line):
                in_braid = False
                if current_block:
                    current_block.braid_data = '\n'.join(braid_lines).strip()
                continue

            # Collect braid content
            if in_braid:
                braid_lines.append(line)
                continue

            # Check for block declaration (::BLOCK_TYPE:: alone on line)
            block_match = self.BLOCK_PATTERN.match(line)
            if block_match:
                block_type = block_match.group(1)
                current_block = HelixBlock(block_type=block_type, raw_content=line)
                doc.blocks.append(current_block)
                continue

            # Check for tag with value (::TAG:: value)
            tag_match = self.TAG_PATTERN.match(line)
            if tag_match:
                tag_name = tag_match.group(1)
                tag_value = tag_match.group(2).strip()

                if current_block is None:
                    # Create implicit block
                    current_block = HelixBlock(block_type='_IMPLICIT')
                    doc.blocks.append(current_block)

                # Handle repeated tags (store in both dict and list)
                if tag_name in current_block.tags:
                    # This is a repeated tag - add to list
                    if tag_name not in current_block.tag_lists:
                        current_block.tag_lists[tag_name] = [current_block.tags[tag_name]]
                    current_block.tag_lists[tag_name].append(tag_value)
                else:
                    current_block.tags[tag_name] = tag_value
                continue

            # Unrecognized line
            if line.startswith('::'):
                self.errors.append(f"Line {i}: Malformed tag: {line}")

        # Extract common metadata to document level
        self._extract_metadata(doc)

        return doc

    def _extract_metadata(self, doc: HelixDocument):
        """Extract common metadata from blocks to document level"""
        metadata_tags = [
            'ENCODER', 'BIRTH_DT', 'ENCODE_TIME', 'SIGNATURE',
            'COSMIC_ANCHOR', 'MASTER_ENTROPY', 'PHI'
        ]

        for tag in metadata_tags:
            value = doc.get_tag(tag)
            if value:
                doc.metadata[tag.lower()] = value

    def parse_file(self, path: str) -> HelixDocument:
        """Parse a .helix file"""
        path = Path(path)
        if not path.exists():
            raise FileNotFoundError(f"File not found: {path}")

        content = path.read_text(encoding='utf-8')
        doc = self.parse(content)
        doc.source_path = str(path)
        return doc


def parse_helix(content: str) -> HelixDocument:
    """Convenience function to parse .helix content"""
    parser = HelixTagParser()
    return parser.parse(content)


def parse_helix_file(path: str) -> HelixDocument:
    """Convenience function to parse a .helix file"""
    parser = HelixTagParser()
    return parser.parse_file(path)


# CLI for testing
if __name__ == "__main__":
    import sys
    import json

    if len(sys.argv) < 2:
        # Demo with inline content
        demo_content = """
::COSMIC_HELIX_DNA::
::ENCODER:: Cosmic Entropy Anchors v1.0
::BIRTH_DT:: 1986-03-03T14:33:00
::ZODIAC:: Pisces Sun
::BLOOD_TYPE:: O+
::COSMIC_ANCHOR:: b385f62823bb854f19e3ad77410bd59f
::BRAID_START::

CGZLAZ

::BRAID_END::
::SIGNATURE:: Born 1986-03-03T14:33:00 at 38.7521N
"""
        doc = parse_helix(demo_content)
        print("Demo parse:")
    else:
        doc = parse_helix_file(sys.argv[1])
        print(f"Parsed: {sys.argv[1]}")

    print(f"  Blocks: {len(doc.blocks)}")
    for block in doc.blocks:
        print(f"    [{block.block_type}] {len(block.tags)} tags")
        for tag, val in block.tags.items():
            print(f"      ::{tag}:: {val[:50]}{'...' if len(val) > 50 else ''}")
        if block.braid_data:
            print(f"      BRAID: {block.braid_data[:30]}...")

    print(f"  Metadata: {json.dumps(doc.metadata, indent=2)}")
    print(f"  Braid: {doc.get_braid()}")
