# Org Chat Logging & Decision Extraction Endpoints
# To be appended to memory-service/app/main.py

import re
from typing import Optional, List
from pydantic import BaseModel
from datetime import datetime, date


# ============================================================================
# ORG CHAT LOGGING
# ============================================================================

class OrgChatMessageCreate(BaseModel):
    chat_id: int
    chat_type: str  # official_ops, mentor_room, public_community
    chat_title: Optional[str] = None
    message_id: int
    sender_telegram_id: Optional[int] = None
    sender_account_id: Optional[str] = None  # UUID if linked
    sender_username: Optional[str] = None
    sender_display_name: Optional[str] = None
    text: Optional[str] = None
    has_media: bool = False
    media_type: Optional[str] = None
    reply_to_message_id: Optional[int] = None
    message_at: datetime


class DecisionRecord(BaseModel):
    decision: str
    action: Optional[str] = None
    owner: Optional[str] = None
    due_date: Optional[date] = None
    canon_change: bool = False


# Decision extraction patterns
DECISION_PATTERNS = {
    'decision': re.compile(r'DECISION:\s*(.+?)(?=\n[A-Z]+:|$)', re.IGNORECASE | re.DOTALL),
    'action': re.compile(r'ACTION:\s*(.+?)(?=\n[A-Z]+:|$)', re.IGNORECASE | re.DOTALL),
    'owner': re.compile(r'OWNER:\s*(@?\w+)', re.IGNORECASE),
    'due': re.compile(r'DUE:\s*(\d{4}-\d{2}-\d{2}|\d{2}\.\d{2}\.\d{4})', re.IGNORECASE),
    'canon_change': re.compile(r'CANON_CHANGE:\s*(yes|true|так|1)', re.IGNORECASE),
}


def extract_decision_from_text(text: str) -> Optional[DecisionRecord]:
    """Extract structured decision from message text."""
    if not text:
        return None
    
    # Check if this looks like a decision
    if 'DECISION:' not in text.upper():
        return None
    
    decision_match = DECISION_PATTERNS['decision'].search(text)
    if not decision_match:
        return None
    
    decision_text = decision_match.group(1).strip()
    
    action_match = DECISION_PATTERNS['action'].search(text)
    owner_match = DECISION_PATTERNS['owner'].search(text)
    due_match = DECISION_PATTERNS['due'].search(text)
    canon_match = DECISION_PATTERNS['canon_change'].search(text)
    
    due_date = None
    if due_match:
        date_str = due_match.group(1)
        try:
            if '-' in date_str:
                due_date = datetime.strptime(date_str, '%Y-%m-%d').date()
            else:
                due_date = datetime.strptime(date_str, '%d.%m.%Y').date()
        except ValueError:
            pass
    
    return DecisionRecord(
        decision=decision_text,
        action=action_match.group(1).strip() if action_match else None,
        owner=owner_match.group(1) if owner_match else None,
        due_date=due_date,
        canon_change=bool(canon_match)
    )


@app.post("/org-chat/message")
async def log_org_chat_message(msg: OrgChatMessageCreate):
    """
    Log a message from an organizational chat.
    Automatically extracts decisions if present.
    """
    try:
        # Insert message
        await db.pool.execute(
            """
            INSERT INTO org_chat_messages (
                chat_id, chat_type, chat_title, message_id,
                sender_telegram_id, sender_account_id, sender_username, sender_display_name,
                text, has_media, media_type, reply_to_message_id, message_at
            ) VALUES ($1, $2, $3, $4, $5, $6::uuid, $7, $8, $9, $10, $11, $12, $13)
            ON CONFLICT (chat_id, message_id) DO UPDATE SET
                text = EXCLUDED.text,
                has_media = EXCLUDED.has_media
            """,
            msg.chat_id, msg.chat_type, msg.chat_title, msg.message_id,
            msg.sender_telegram_id, msg.sender_account_id, msg.sender_username, msg.sender_display_name,
            msg.text, msg.has_media, msg.media_type, msg.reply_to_message_id, msg.message_at
        )
        
        # Try to extract decision
        decision = None
        if msg.text:
            decision = extract_decision_from_text(msg.text)
            if decision:
                await db.pool.execute(
                    """
                    INSERT INTO decision_records (
                        chat_id, source_message_id, decision, action, owner, due_date, canon_change
                    ) VALUES ($1, $2, $3, $4, $5, $6, $7)
                    """,
                    msg.chat_id, msg.message_id, decision.decision, decision.action,
                    decision.owner, decision.due_date, decision.canon_change
                )
                logger.info(f"Decision extracted from message {msg.message_id} in chat {msg.chat_id}")
        
        return {
            "success": True, 
            "message_id": msg.message_id,
            "decision_extracted": decision is not None
        }
        
    except Exception as e:
        logger.error(f"Failed to log org chat message: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/org-chat/{chat_id}/messages")
async def get_org_chat_messages(
    chat_id: int,
    limit: int = Query(default=50, le=200),
    since: Optional[datetime] = None
):
    """Get messages from an organizational chat."""
    try:
        if since:
            rows = await db.pool.fetch(
                """
                SELECT * FROM org_chat_messages 
                WHERE chat_id = $1 AND message_at > $2
                ORDER BY message_at DESC LIMIT $3
                """,
                chat_id, since, limit
            )
        else:
            rows = await db.pool.fetch(
                """
                SELECT * FROM org_chat_messages 
                WHERE chat_id = $1
                ORDER BY message_at DESC LIMIT $2
                """,
                chat_id, limit
            )
        
        return {"messages": [dict(r) for r in rows], "count": len(rows)}
        
    except Exception as e:
        logger.error(f"Failed to get org chat messages: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/decisions")
async def get_decisions(
    status: Optional[str] = None,
    chat_id: Optional[int] = None,
    canon_only: bool = False,
    overdue_only: bool = False,
    limit: int = Query(default=20, le=100)
):
    """Get decision records with filters."""
    try:
        conditions = []
        params = []
        param_idx = 1
        
        if status:
            conditions.append(f"status = ${param_idx}")
            params.append(status)
            param_idx += 1
        
        if chat_id:
            conditions.append(f"chat_id = ${param_idx}")
            params.append(chat_id)
            param_idx += 1
        
        if canon_only:
            conditions.append("canon_change = true")
        
        if overdue_only:
            conditions.append(f"due_date < ${param_idx} AND status NOT IN ('completed', 'cancelled')")
            params.append(date.today())
            param_idx += 1
        
        where_clause = " AND ".join(conditions) if conditions else "1=1"
        params.append(limit)
        
        rows = await db.pool.fetch(
            f"""
            SELECT dr.*, ocm.text as source_text, ocm.sender_username
            FROM decision_records dr
            LEFT JOIN org_chat_messages ocm ON dr.chat_id = ocm.chat_id AND dr.source_message_id = ocm.message_id
            WHERE {where_clause}
            ORDER BY dr.created_at DESC
            LIMIT ${param_idx}
            """,
            *params
        )
        
        return {"decisions": [dict(r) for r in rows], "count": len(rows)}
        
    except Exception as e:
        logger.error(f"Failed to get decisions: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.patch("/decisions/{decision_id}/status")
async def update_decision_status(
    decision_id: str,
    status: str,
    updated_by: Optional[str] = None
):
    """Update decision status (pending, in_progress, completed, cancelled)."""
    try:
        valid_statuses = ['pending', 'in_progress', 'completed', 'cancelled']
        if status not in valid_statuses:
            raise HTTPException(status_code=400, detail=f"Invalid status. Must be one of: {valid_statuses}")
        
        await db.pool.execute(
            """
            UPDATE decision_records 
            SET status = $1, status_updated_at = NOW(), status_updated_by = $2
            WHERE id = $3::uuid
            """,
            status, updated_by, decision_id
        )
        
        return {"success": True, "decision_id": decision_id, "new_status": status}
        
    except Exception as e:
        logger.error(f"Failed to update decision status: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/decisions/summary")
async def get_decisions_summary():
    """Get summary of decisions by status."""
    try:
        rows = await db.pool.fetch(
            """
            SELECT 
                status,
                COUNT(*) as count,
                COUNT(*) FILTER (WHERE due_date < CURRENT_DATE AND status NOT IN ('completed', 'cancelled')) as overdue
            FROM decision_records
            GROUP BY status
            """
        )
        
        summary = {r['status']: {'count': r['count'], 'overdue': r['overdue']} for r in rows}
        
        # Count canon changes
        canon_count = await db.pool.fetchval(
            "SELECT COUNT(*) FROM decision_records WHERE canon_change = true"
        )
        
        return {
            "by_status": summary,
            "canon_changes": canon_count,
            "total": sum(s['count'] for s in summary.values())
        }
        
    except Exception as e:
        logger.error(f"Failed to get decisions summary: {e}")
        raise HTTPException(status_code=500, detail=str(e))
