#!/usr/bin/env python3
"""
HITL (Human-In-The-Loop) Approval Workflow for Genesis.

All customer-facing outputs MUST be approved by Kinan before deployment.
This ensures quality, brand consistency, and prevents automation mishaps.

Key Principle: Genesis prepares, Kinan approves, then Genesis deploys.
"""

import json
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, asdict
from enum import Enum
import hashlib

sys.path.insert(0, '/mnt/e/genesis-system')
sys.path.insert(0, '/mnt/e/genesis-system/data/genesis-memory')

# Database imports
try:
    from elestio_config import PostgresConfig
    import psycopg2
    from psycopg2.extras import Json
    HAS_DB = True
except ImportError:
    HAS_DB = False
    print("Warning: PostgreSQL not available, using file-based queue")


class ApprovalStatus(Enum):
    """Status of an approval request."""
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"
    REVISION_REQUESTED = "revision_requested"
    EXPIRED = "expired"


class OutputType(Enum):
    """Types of customer-facing outputs requiring approval."""
    EMAIL_OUTREACH = "email_outreach"
    SMS_CAMPAIGN = "sms_campaign"
    VOICE_SCRIPT = "voice_script"
    PROPOSAL = "proposal"
    QUOTE = "quote"
    CONTRACT = "contract"
    SOCIAL_POST = "social_post"
    AD_COPY = "ad_copy"
    LANDING_PAGE = "landing_page"
    CHATBOT_RESPONSE = "chatbot_response"
    AUDIT_REPORT = "audit_report"
    ONBOARDING_SEQUENCE = "onboarding_sequence"


@dataclass
class ApprovalRequest:
    """A request for human approval of customer-facing output."""
    request_id: str
    output_type: OutputType
    title: str
    content: str
    target_audience: str
    context: Dict[str, Any]
    created_at: str
    status: ApprovalStatus = ApprovalStatus.PENDING
    reviewed_at: Optional[str] = None
    reviewer_notes: Optional[str] = None
    revision_count: int = 0
    priority: str = "normal"  # low, normal, high, urgent

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for storage."""
        d = asdict(self)
        d['output_type'] = self.output_type.value
        d['status'] = self.status.value
        return d

    @classmethod
    def from_dict(cls, d: Dict[str, Any]) -> 'ApprovalRequest':
        """Create from dictionary."""
        d['output_type'] = OutputType(d['output_type'])
        d['status'] = ApprovalStatus(d['status'])
        return cls(**d)


class HITLApprovalQueue:
    """
    Human-In-The-Loop Approval Queue.

    All customer-facing outputs flow through this queue:
    1. Genesis generates content
    2. Content enters approval queue
    3. Kinan reviews and approves/rejects/requests revision
    4. Only approved content gets deployed
    """

    QUEUE_DIR = Path("/mnt/e/genesis-system/data/hitl_queue")

    def __init__(self):
        """Initialize the HITL approval queue."""
        self.QUEUE_DIR.mkdir(parents=True, exist_ok=True)

        if HAS_DB:
            self._init_database()

    def _init_database(self):
        """Initialize PostgreSQL tables for HITL queue."""
        try:
            conn = psycopg2.connect(**PostgresConfig.get_connection_params())
            cur = conn.cursor()

            cur.execute("""
                CREATE TABLE IF NOT EXISTS hitl_approval_queue (
                    request_id TEXT PRIMARY KEY,
                    output_type TEXT NOT NULL,
                    title TEXT NOT NULL,
                    content TEXT NOT NULL,
                    target_audience TEXT,
                    context JSONB,
                    created_at TIMESTAMP DEFAULT NOW(),
                    status TEXT DEFAULT 'pending',
                    reviewed_at TIMESTAMP,
                    reviewer_notes TEXT,
                    revision_count INTEGER DEFAULT 0,
                    priority TEXT DEFAULT 'normal'
                )
            """)

            cur.execute("""
                CREATE INDEX IF NOT EXISTS idx_hitl_status
                ON hitl_approval_queue(status)
            """)

            cur.execute("""
                CREATE INDEX IF NOT EXISTS idx_hitl_priority
                ON hitl_approval_queue(priority, created_at)
            """)

            conn.commit()
            cur.close()
            conn.close()
        except Exception as e:
            print(f"Database init error: {e}")

    def _generate_request_id(self, output_type: OutputType, content: str) -> str:
        """Generate unique request ID."""
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        content_hash = hashlib.md5(content.encode()).hexdigest()[:8]
        return f"HITL-{output_type.value.upper()[:4]}-{timestamp}-{content_hash}"

    def submit_for_approval(
        self,
        output_type: OutputType,
        title: str,
        content: str,
        target_audience: str,
        context: Optional[Dict[str, Any]] = None,
        priority: str = "normal"
    ) -> ApprovalRequest:
        """
        Submit content for human approval.

        Args:
            output_type: Type of content (email, SMS, voice script, etc.)
            title: Brief title describing the content
            content: The actual content to be approved
            target_audience: Who will receive this content
            context: Additional context (campaign info, lead data, etc.)
            priority: Approval urgency (low, normal, high, urgent)

        Returns:
            ApprovalRequest with pending status
        """
        request_id = self._generate_request_id(output_type, content)

        request = ApprovalRequest(
            request_id=request_id,
            output_type=output_type,
            title=title,
            content=content,
            target_audience=target_audience,
            context=context or {},
            created_at=datetime.now().isoformat(),
            status=ApprovalStatus.PENDING,
            priority=priority
        )

        # Save to database if available
        if HAS_DB:
            self._save_to_db(request)

        # Always save to file for backup/visibility
        self._save_to_file(request)

        print(f"[HITL] Submitted for approval: {request_id}")
        print(f"       Type: {output_type.value}")
        print(f"       Title: {title}")
        print(f"       Priority: {priority}")

        return request

    def _save_to_db(self, request: ApprovalRequest):
        """Save request to PostgreSQL."""
        try:
            conn = psycopg2.connect(**PostgresConfig.get_connection_params())
            cur = conn.cursor()

            cur.execute("""
                INSERT INTO hitl_approval_queue
                (request_id, output_type, title, content, target_audience,
                 context, created_at, status, priority)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
                ON CONFLICT (request_id) DO UPDATE SET
                    status = EXCLUDED.status,
                    reviewed_at = EXCLUDED.reviewed_at,
                    reviewer_notes = EXCLUDED.reviewer_notes,
                    revision_count = EXCLUDED.revision_count
            """, (
                request.request_id,
                request.output_type.value,
                request.title,
                request.content,
                request.target_audience,
                Json(request.context),
                request.created_at,
                request.status.value,
                request.priority
            ))

            conn.commit()
            cur.close()
            conn.close()
        except Exception as e:
            print(f"Database save error: {e}")

    def _save_to_file(self, request: ApprovalRequest):
        """Save request to file for visibility."""
        # Organize by status
        status_dir = self.QUEUE_DIR / request.status.value
        status_dir.mkdir(exist_ok=True)

        file_path = status_dir / f"{request.request_id}.json"
        with open(file_path, 'w') as f:
            json.dump(request.to_dict(), f, indent=2)

    def get_pending_requests(self, priority: Optional[str] = None) -> List[ApprovalRequest]:
        """Get all pending approval requests."""
        requests = []
        pending_dir = self.QUEUE_DIR / "pending"

        if pending_dir.exists():
            for file_path in pending_dir.glob("*.json"):
                with open(file_path, 'r') as f:
                    data = json.load(f)
                    request = ApprovalRequest.from_dict(data)
                    if priority is None or request.priority == priority:
                        requests.append(request)

        # Sort by priority and creation time
        priority_order = {"urgent": 0, "high": 1, "normal": 2, "low": 3}
        requests.sort(key=lambda r: (priority_order.get(r.priority, 2), r.created_at))

        return requests

    def approve(self, request_id: str, notes: Optional[str] = None) -> bool:
        """
        Approve a pending request.

        Args:
            request_id: The request to approve
            notes: Optional approval notes

        Returns:
            True if approved successfully
        """
        return self._update_status(request_id, ApprovalStatus.APPROVED, notes)

    def reject(self, request_id: str, reason: str) -> bool:
        """
        Reject a pending request.

        Args:
            request_id: The request to reject
            reason: Why it was rejected

        Returns:
            True if rejected successfully
        """
        return self._update_status(request_id, ApprovalStatus.REJECTED, reason)

    def request_revision(self, request_id: str, feedback: str) -> bool:
        """
        Request revision of a pending request.

        Args:
            request_id: The request needing revision
            feedback: What needs to change

        Returns:
            True if updated successfully
        """
        return self._update_status(request_id, ApprovalStatus.REVISION_REQUESTED, feedback)

    def _update_status(
        self,
        request_id: str,
        new_status: ApprovalStatus,
        notes: Optional[str]
    ) -> bool:
        """Update the status of a request."""
        # Find the request
        pending_dir = self.QUEUE_DIR / "pending"
        file_path = pending_dir / f"{request_id}.json"

        if not file_path.exists():
            print(f"[HITL] Request not found: {request_id}")
            return False

        with open(file_path, 'r') as f:
            data = json.load(f)

        request = ApprovalRequest.from_dict(data)
        request.status = new_status
        request.reviewed_at = datetime.now().isoformat()
        request.reviewer_notes = notes

        if new_status == ApprovalStatus.REVISION_REQUESTED:
            request.revision_count += 1

        # Move to new status directory
        file_path.unlink()
        self._save_to_file(request)

        if HAS_DB:
            self._save_to_db(request)

        print(f"[HITL] Updated {request_id} to {new_status.value}")
        return True

    def check_approval(self, request_id: str) -> Optional[ApprovalRequest]:
        """
        Check the status of an approval request.

        Args:
            request_id: The request to check

        Returns:
            ApprovalRequest if found, None otherwise
        """
        for status in ApprovalStatus:
            status_dir = self.QUEUE_DIR / status.value
            file_path = status_dir / f"{request_id}.json"

            if file_path.exists():
                with open(file_path, 'r') as f:
                    data = json.load(f)
                return ApprovalRequest.from_dict(data)

        return None

    def is_approved(self, request_id: str) -> bool:
        """Check if a request has been approved."""
        request = self.check_approval(request_id)
        return request is not None and request.status == ApprovalStatus.APPROVED

    def get_queue_summary(self) -> Dict[str, int]:
        """Get summary counts of queue by status."""
        summary = {}
        for status in ApprovalStatus:
            status_dir = self.QUEUE_DIR / status.value
            if status_dir.exists():
                summary[status.value] = len(list(status_dir.glob("*.json")))
            else:
                summary[status.value] = 0
        return summary


# Convenience functions for quick access
_queue_instance: Optional[HITLApprovalQueue] = None

def get_hitl_queue() -> HITLApprovalQueue:
    """Get the singleton HITL queue instance."""
    global _queue_instance
    if _queue_instance is None:
        _queue_instance = HITLApprovalQueue()
    return _queue_instance


def submit_for_approval(
    output_type: str,
    title: str,
    content: str,
    target_audience: str,
    context: Optional[Dict[str, Any]] = None,
    priority: str = "normal"
) -> ApprovalRequest:
    """
    Quick function to submit content for approval.

    Example:
        from core.hitl_approval import submit_for_approval, OutputType

        request = submit_for_approval(
            output_type="email_outreach",
            title="Initial outreach to Cairns plumber",
            content="Hi John, I noticed your business...",
            target_audience="John's Plumbing, Cairns",
            priority="high"
        )
    """
    queue = get_hitl_queue()
    return queue.submit_for_approval(
        output_type=OutputType(output_type),
        title=title,
        content=content,
        target_audience=target_audience,
        context=context,
        priority=priority
    )


def check_approval(request_id: str) -> Optional[ApprovalRequest]:
    """Check if a request has been approved."""
    return get_hitl_queue().check_approval(request_id)


def is_approved(request_id: str) -> bool:
    """Quick check if content is approved for deployment."""
    return get_hitl_queue().is_approved(request_id)


def require_approval(output_type: str):
    """
    Decorator to require HITL approval before function execution.

    Example:
        @require_approval("email_outreach")
        def send_outreach_email(lead, content):
            # This will only execute if content is approved
            ...
    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            # Check if there's an approval_request_id in kwargs
            request_id = kwargs.get('approval_request_id')
            if not request_id:
                raise ValueError(
                    f"Customer-facing output requires HITL approval. "
                    f"Submit content via submit_for_approval() first."
                )

            if not is_approved(request_id):
                request = check_approval(request_id)
                if request:
                    status = request.status.value
                    raise ValueError(
                        f"Content not approved. Current status: {status}. "
                        f"Request ID: {request_id}"
                    )
                else:
                    raise ValueError(f"Approval request not found: {request_id}")

            return func(*args, **kwargs)
        return wrapper
    return decorator


# CLI interface for Kinan to review queue
def review_queue():
    """Interactive CLI for reviewing the approval queue."""
    queue = get_hitl_queue()

    print("\n" + "="*60)
    print("HITL APPROVAL QUEUE")
    print("="*60)

    summary = queue.get_queue_summary()
    print(f"\nQueue Status:")
    for status, count in summary.items():
        print(f"  {status}: {count}")

    pending = queue.get_pending_requests()

    if not pending:
        print("\nNo pending requests.")
        return

    print(f"\n{len(pending)} pending request(s):\n")

    for i, request in enumerate(pending, 1):
        print(f"[{i}] {request.request_id}")
        print(f"    Type: {request.output_type.value}")
        print(f"    Title: {request.title}")
        print(f"    Target: {request.target_audience}")
        print(f"    Priority: {request.priority}")
        print(f"    Created: {request.created_at}")
        print("-" * 40)
        print(f"    Content Preview:")
        preview = request.content[:200] + "..." if len(request.content) > 200 else request.content
        for line in preview.split('\n'):
            print(f"    | {line}")
        print("-" * 40)
        print()


if __name__ == "__main__":
    # Demo/test
    review_queue()
