"""
Ticket Shell - Ticket management with spatial context
Standalone version for cubesquare package.

NO Python for-loops in hot paths.
ALL batch operations use C backend via _core.
ZERO dependencies on pipe_v001, CreatureGarage, or hou.

Provides ticket management with Morton spatial context
and optional LLM summaries.

HEX_ID: 5449434B (TICK)
"""

import json
import sqlite3
from pathlib import Path
from typing import Dict, List, Optional, Any
from datetime import datetime

# Import from C backend
from .._core import PHI, K5, K11, PRIME_131, LIGHT_UNIT
from .._core import morton_decode

# LLM (optional)
try:
    import ollama
    OLLAMA_AVAILABLE = True
except ImportError:
    OLLAMA_AVAILABLE = False

# Paths
PACKAGE_ROOT = Path(__file__).parent.parent.parent
DATA_DIR = PACKAGE_ROOT / "data"
DB_PATH = DATA_DIR / "tickets.db"


class TicketShell:
    """
    Ticket management with spatial context.

    Layers: L2 (Semantic), L3 (Spatial)
    Basin range: 15-43
    I-Logic state: DIRECT (tracking)

    Manages tickets with Morton-encoded spatial context
    and optional LLM enhancement.
    """

    HEX_ID = "5449434B"

    def __init__(self, db_path: Path = None):
        """Initialize shell."""
        self.db_path = db_path or DB_PATH
        self.model = "phi4-mini"
        self._ensure_db()

    def _ensure_db(self):
        """Ensure database exists with proper schema."""
        if not self.db_path.parent.exists():
            self.db_path.parent.mkdir(parents=True, exist_ok=True)

        conn = sqlite3.connect(str(self.db_path))
        conn.execute("""
            CREATE TABLE IF NOT EXISTS tickets (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                ticket_number TEXT UNIQUE,
                title TEXT,
                description TEXT,
                status TEXT DEFAULT 'open',
                priority TEXT DEFAULT 'medium',
                type TEXT DEFAULT 'feature',
                hex_id TEXT,
                created_at TEXT,
                updated_at TEXT
            )
        """)
        conn.execute("""
            CREATE TABLE IF NOT EXISTS ticket_relationships (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                ticket_id INTEGER,
                related_ticket_id INTEGER,
                relationship_type TEXT,
                FOREIGN KEY (ticket_id) REFERENCES tickets(id),
                FOREIGN KEY (related_ticket_id) REFERENCES tickets(id)
            )
        """)
        conn.commit()
        conn.close()

    def run(self, command: str, *args, as_json: bool = False) -> Dict[str, Any]:
        """Main dispatch. Called by CLI and MCP."""
        commands = {
            'status': self.status,
            'get': self.get_ticket,
            'list': self.list_tickets,
            'create': self.create_ticket,
            'enhance': self.enhance_ticket,
            'spatial': self.get_spatial_context,
            'help': self.help,
        }
        fn = commands.get(command, self.status)
        return fn(*args, as_json=as_json)

    def status(self, *args, as_json: bool = False) -> Dict[str, Any]:
        """Get ticket system status."""
        conn = self._get_conn()
        stats = {
            'shell': 'ticket',
            'hex_id': self.HEX_ID,
            'version': '1.0.0',
            'db_exists': conn is not None,
            'db_path': str(self.db_path),
            'c_backend': True,
            'hot_path_loops': 0,
            'cg_dependencies': 0,
            'llm_available': OLLAMA_AVAILABLE,
            'model': self.model
        }

        if conn:
            try:
                cur = conn.cursor()
                cur.execute("SELECT COUNT(*) FROM tickets")
                stats['total_tickets'] = cur.fetchone()[0]

                cur.execute("SELECT status, COUNT(*) FROM tickets GROUP BY status")
                stats['by_status'] = {r[0]: r[1] for r in cur.fetchall()}

                cur.execute("SELECT COUNT(*) FROM ticket_relationships")
                stats['total_relationships'] = cur.fetchone()[0]
            except Exception:
                pass
            finally:
                conn.close()

        return stats

    def help(self, *args, as_json: bool = False) -> Dict[str, Any]:
        """Show help."""
        return {
            'commands': {
                'status': 'System status',
                'get <number>': 'Get ticket by number',
                'list': 'List recent tickets',
                'create <title>': 'Create new ticket',
                'enhance <number>': 'Enhance ticket with relationships',
                'spatial <hex_id>': 'Get spatial context from hex ID'
            },
            'usage': 'cubesquare shell ticket <command> [args]'
        }

    def _get_conn(self):
        """Get database connection."""
        if not self.db_path.exists():
            return None
        return sqlite3.connect(str(self.db_path))

    def get_ticket(self, ticket_number: str = None, *args, as_json: bool = False) -> Optional[Dict[str, Any]]:
        """Get ticket by number."""
        if not ticket_number:
            return {'error': 'Ticket number required'}

        conn = self._get_conn()
        if not conn:
            return {'error': 'Database not found'}

        try:
            conn.row_factory = sqlite3.Row
            cur = conn.cursor()
            cur.execute("""
                SELECT * FROM tickets WHERE ticket_number = ?
            """, (ticket_number,))
            row = cur.fetchone()
            return dict(row) if row else {'error': f'Ticket {ticket_number} not found'}
        finally:
            conn.close()

    def list_tickets(self, status: str = None, *args, as_json: bool = False) -> List[Dict]:
        """List tickets with optional status filter."""
        conn = self._get_conn()
        if not conn:
            return []

        try:
            conn.row_factory = sqlite3.Row
            cur = conn.cursor()

            if status and status != 'all':
                cur.execute("""
                    SELECT ticket_number, title, status, priority, hex_id
                    FROM tickets WHERE status = ?
                    ORDER BY updated_at DESC LIMIT 20
                """, (status,))
            else:
                cur.execute("""
                    SELECT ticket_number, title, status, priority, hex_id
                    FROM tickets
                    ORDER BY updated_at DESC LIMIT 20
                """)

            return [dict(r) for r in cur.fetchall()]
        finally:
            conn.close()

    def create_ticket(self, title: str = None, *args, as_json: bool = False) -> Dict[str, Any]:
        """Create a new ticket."""
        if not title:
            return {'error': 'Title required'}

        conn = self._get_conn()
        if not conn:
            self._ensure_db()
            conn = self._get_conn()

        try:
            cur = conn.cursor()

            # Generate ticket number
            cur.execute("SELECT COUNT(*) FROM tickets")
            count = cur.fetchone()[0]
            ticket_number = f"T-{count + 1:04d}"

            # Generate hex ID from title hash
            import hashlib
            hash_bytes = hashlib.sha256(title.encode()).digest()
            hex_id = hash_bytes[:4].hex().upper()

            now = datetime.now().isoformat()

            cur.execute("""
                INSERT INTO tickets (ticket_number, title, hex_id, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?)
            """, (ticket_number, title, hex_id, now, now))
            conn.commit()

            return {
                'success': True,
                'ticket_number': ticket_number,
                'title': title,
                'hex_id': hex_id,
                'status': 'open',
                'spatial': self.get_spatial_context(hex_id)
            }
        finally:
            conn.close()

    def get_spatial_context(self, hex_id: str = None, *args, as_json: bool = False) -> Dict[str, Any]:
        """Get spatial context from hex ID using Morton decoding."""
        if not hex_id or len(hex_id) < 8:
            return {'error': 'Invalid hex_id (need 8 characters)'}

        try:
            # Parse hex ID as Morton code
            morton = int(hex_id, 16)

            # Use C backend for decode
            x, y, z = morton_decode(morton)
            x, y, z = x % 48, y % 48, z % 48

            # Compute basin and layer
            basin = (x + y + z) % PRIME_131
            layer = (basin % 9) + 1
            k11 = ((x + y + z) % 23) - 11

            # Compute diagonal
            diagonal = ((x * x + y * y + z * z) ** 0.5)

            return {
                'hex_id': hex_id,
                'morton': morton,
                'coords': (x, y, z),
                'basin': basin,
                'layer': f'L{layer}',
                'k11_level': k11,
                'diagonal': round(diagonal, 4),
                'phi_ratio': round(diagonal / PHI if diagonal > 0 else 0, 6)
            }
        except Exception as e:
            return {'error': f'Decode failed: {e}'}

    def enhance_ticket(self, ticket_number: str = None, *args, as_json: bool = False) -> Dict[str, Any]:
        """Enhance ticket with relationships and spatial context."""
        if not ticket_number:
            return {'error': 'Ticket number required'}

        ticket = self.get_ticket(ticket_number)
        if 'error' in ticket:
            return ticket

        # Get spatial context
        spatial = self.get_spatial_context(ticket.get('hex_id', ''))

        # Build enhancement
        enhanced = {
            'ticket': {
                'number': ticket.get('ticket_number'),
                'title': ticket.get('title'),
                'status': ticket.get('status'),
                'priority': ticket.get('priority', 'medium'),
                'type': ticket.get('type', 'feature'),
                'hex_id': ticket.get('hex_id', ''),
                'description': (ticket.get('description') or '')[:500]
            },
            'spatial': spatial if 'error' not in spatial else {}
        }

        # Get relationships
        conn = self._get_conn()
        if conn:
            try:
                conn.row_factory = sqlite3.Row
                cur = conn.cursor()
                cur.execute("""
                    SELECT tr.*, t2.title as related_title, t2.status as related_status
                    FROM ticket_relationships tr
                    JOIN tickets t1 ON tr.ticket_id = t1.id
                    JOIN tickets t2 ON tr.related_ticket_id = t2.id
                    WHERE t1.ticket_number = ?
                """, (ticket_number,))
                relationships = [dict(r) for r in cur.fetchall()]
                enhanced['relationships'] = {
                    'count': len(relationships),
                    'items': relationships[:10]
                }
            except Exception:
                enhanced['relationships'] = {'count': 0, 'items': []}
            finally:
                conn.close()

        # Generate LLM summary if available
        if OLLAMA_AVAILABLE:
            enhanced['summary'] = self._generate_summary(enhanced)

        return enhanced

    def _generate_summary(self, enhanced: Dict) -> str:
        """Generate LLM summary of ticket context."""
        try:
            ticket = enhanced['ticket']
            spatial = enhanced.get('spatial', {})
            prompt = f"""Summarize this ticket in 2-3 sentences:
Title: {ticket['title']}
Status: {ticket['status']} | Priority: {ticket['priority']}
Description: {ticket['description'][:300] if ticket['description'] else 'No description'}
Spatial: Basin {spatial.get('basin', 'N/A')}, {spatial.get('layer', 'N/A')}"""

            response = ollama.generate(model=self.model, prompt=prompt)
            return response.response[:500] if hasattr(response, 'response') else str(response)[:500]
        except Exception as e:
            return f"Summary unavailable: {e}"
