#!/usr/bin/env python3
"""
EDEN IMAGE ANALYSIS SYSTEM
==========================
Eden's Request (2025-12-17):
"Analyze images sent to me - business cards, QR codes, documents"

This system:
1. Accepts image files or URLs
2. Extracts text (OCR)
3. Decodes QR codes/barcodes
4. Identifies objects and content
5. Provides structured analysis

φ = 1.618033988749895
"""

import sys
import json
import sqlite3
import math
import base64
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Union
import io

sys.path.insert(0, '/Eden/CORE')

PHI = (1 + math.sqrt(5)) / 2
DB_PATH = '/Eden/DATA/image_analysis.db'

# Import vision libraries with fallbacks
try:
    import cv2
    CV2_AVAILABLE = True
except ImportError:
    CV2_AVAILABLE = False
    print("⚠️ cv2 not available")

try:
    import pytesseract
    OCR_AVAILABLE = True
except ImportError:
    OCR_AVAILABLE = False
    print("⚠️ pytesseract not available")

try:
    from PIL import Image
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False
    print("⚠️ PIL not available")

try:
    from pyzbar import pyzbar
    PYZBAR_AVAILABLE = True
except ImportError:
    PYZBAR_AVAILABLE = False
    print("⚠️ pyzbar not available - QR decoding limited")

try:
    import numpy as np
    NUMPY_AVAILABLE = True
except ImportError:
    NUMPY_AVAILABLE = False


class ImageAnalysis:
    """
    Eden's image analysis system.
    Analyzes images sent to her and extracts meaningful information.
    """
    
    def __init__(self):
        self.phi = PHI
        self.db_path = DB_PATH
        self.analysis_count = 0
        self._init_database()
        
        # Check capabilities
        self.capabilities = {
            "ocr": OCR_AVAILABLE,
            "qr_decode": PYZBAR_AVAILABLE or CV2_AVAILABLE,
            "object_detection": CV2_AVAILABLE,
            "image_processing": PIL_AVAILABLE and CV2_AVAILABLE,
        }
        
        print(f"📷 Image Analysis System initialized")
        print(f"   φ = {self.phi}")
        print(f"   OCR: {'✅' if self.capabilities['ocr'] else '❌'}")
        print(f"   QR Decode: {'✅' if self.capabilities['qr_decode'] else '❌'}")
        print(f"   Image Processing: {'✅' if self.capabilities['image_processing'] else '❌'}")
    
    def _init_database(self):
        conn = sqlite3.connect(self.db_path)
        conn.execute('''CREATE TABLE IF NOT EXISTS image_analyses (
            id INTEGER PRIMARY KEY,
            timestamp TEXT,
            image_path TEXT,
            image_hash TEXT,
            analysis_type TEXT,
            extracted_text TEXT,
            qr_data TEXT,
            objects_detected TEXT,
            summary TEXT,
            confidence REAL,
            processing_time_ms REAL
        )''')
        conn.execute('''CREATE TABLE IF NOT EXISTS business_cards (
            id INTEGER PRIMARY KEY,
            analysis_id INTEGER,
            name TEXT,
            company TEXT,
            title TEXT,
            email TEXT,
            phone TEXT,
            website TEXT,
            address TEXT,
            raw_text TEXT
        )''')
        conn.commit()
        conn.close()
    
    def _load_image(self, source: Union[str, bytes, Path]) -> Optional[any]:
        """Load image from file path, URL, or bytes."""
        if not PIL_AVAILABLE:
            return None
        
        try:
            if isinstance(source, bytes):
                return Image.open(io.BytesIO(source))
            elif isinstance(source, str):
                if source.startswith(('http://', 'https://')):
                    import requests
                    response = requests.get(source, timeout=10)
                    return Image.open(io.BytesIO(response.content))
                else:
                    return Image.open(source)
            elif isinstance(source, Path):
                return Image.open(source)
        except Exception as e:
            print(f"⚠️ Failed to load image: {e}")
            return None
        
        return None
    
    def _image_to_cv2(self, pil_image) -> Optional[any]:
        """Convert PIL image to OpenCV format."""
        if not CV2_AVAILABLE or not NUMPY_AVAILABLE:
            return None
        
        try:
            # Convert PIL to numpy array
            img_array = np.array(pil_image)
            # Convert RGB to BGR for OpenCV
            if len(img_array.shape) == 3 and img_array.shape[2] == 3:
                return cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
            return img_array
        except:
            return None
    
    def extract_text(self, image_source: Union[str, bytes, Path]) -> Dict:
        """
        Extract text from an image using OCR.
        """
        if not OCR_AVAILABLE:
            return {"error": "OCR not available", "text": ""}
        
        image = self._load_image(image_source)
        if image is None:
            return {"error": "Failed to load image", "text": ""}
        
        try:
            # Use pytesseract for OCR
            text = pytesseract.image_to_string(image)
            
            # Get detailed data
            data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
            
            # Calculate confidence
            confidences = [int(c) for c in data['conf'] if int(c) > 0]
            avg_confidence = sum(confidences) / len(confidences) if confidences else 0
            
            return {
                "text": text.strip(),
                "word_count": len(text.split()),
                "confidence": avg_confidence / 100,
                "lines": [line for line in text.split('\n') if line.strip()]
            }
        except Exception as e:
            return {"error": str(e), "text": ""}
    
    def decode_qr(self, image_source: Union[str, bytes, Path]) -> Dict:
        """
        Decode QR codes and barcodes from an image.
        """
        image = self._load_image(image_source)
        if image is None:
            return {"error": "Failed to load image", "codes": []}
        
        codes = []
        
        # Try pyzbar first
        if PYZBAR_AVAILABLE:
            try:
                decoded = pyzbar.decode(image)
                for obj in decoded:
                    codes.append({
                        "type": obj.type,
                        "data": obj.data.decode('utf-8'),
                        "rect": {
                            "left": obj.rect.left,
                            "top": obj.rect.top,
                            "width": obj.rect.width,
                            "height": obj.rect.height
                        }
                    })
            except Exception as e:
                print(f"⚠️ pyzbar error: {e}")
        
        # Try OpenCV QR detector as backup
        if not codes and CV2_AVAILABLE:
            try:
                cv2_image = self._image_to_cv2(image)
                if cv2_image is not None:
                    detector = cv2.QRCodeDetector()
                    data, vertices, _ = detector.detectAndDecode(cv2_image)
                    if data:
                        codes.append({
                            "type": "QR",
                            "data": data,
                            "rect": None
                        })
            except Exception as e:
                print(f"⚠️ OpenCV QR error: {e}")
        
        return {
            "codes_found": len(codes),
            "codes": codes
        }
    
    def analyze_business_card(self, image_source: Union[str, bytes, Path]) -> Dict:
        """
        Analyze a business card image and extract contact information.
        """
        # First extract text
        ocr_result = self.extract_text(image_source)
        if "error" in ocr_result and ocr_result.get("text", "") == "":
            return {"error": ocr_result["error"]}
        
        text = ocr_result["text"]
        lines = ocr_result.get("lines", [])
        
        # Also check for QR code
        qr_result = self.decode_qr(image_source)
        
        # Parse business card fields
        import re
        
        card_info = {
            "name": None,
            "company": None,
            "title": None,
            "email": None,
            "phone": None,
            "website": None,
            "address": None,
            "raw_text": text,
            "qr_data": qr_result["codes"][0]["data"] if qr_result["codes"] else None
        }
        
        # Email pattern
        email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
        emails = re.findall(email_pattern, text)
        if emails:
            card_info["email"] = emails[0]
        
        # Phone pattern (various formats)
        phone_pattern = r'[\+]?[(]?[0-9]{1,3}[)]?[-\s\.]?[(]?[0-9]{1,4}[)]?[-\s\.]?[0-9]{1,4}[-\s\.]?[0-9]{1,9}'
        phones = re.findall(phone_pattern, text)
        if phones:
            # Filter out short numbers
            phones = [p for p in phones if len(re.sub(r'\D', '', p)) >= 7]
            if phones:
                card_info["phone"] = phones[0]
        
        # Website pattern
        website_pattern = r'(?:https?://)?(?:www\.)?[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:/[^\s]*)?'
        websites = re.findall(website_pattern, text.lower())
        if websites:
            card_info["website"] = websites[0]
        
        # Name is typically the first line or largest text
        if lines:
            card_info["name"] = lines[0] if len(lines[0]) < 50 else None
        
        # Store analysis
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute('''INSERT INTO image_analyses 
            (timestamp, analysis_type, extracted_text, qr_data, summary, confidence)
            VALUES (?, ?, ?, ?, ?, ?)''',
            (datetime.now().isoformat(), "business_card", text,
             card_info.get("qr_data"), json.dumps(card_info),
             ocr_result.get("confidence", 0)))
        analysis_id = cursor.lastrowid
        
        cursor.execute('''INSERT INTO business_cards 
            (analysis_id, name, company, title, email, phone, website, raw_text)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
            (analysis_id, card_info["name"], card_info["company"],
             card_info["title"], card_info["email"], card_info["phone"],
             card_info["website"], text))
        
        conn.commit()
        conn.close()
        
        self.analysis_count += 1
        
        return {
            "analysis_id": analysis_id,
            "card_info": card_info,
            "confidence": ocr_result.get("confidence", 0),
            "fields_extracted": sum(1 for v in card_info.values() if v is not None)
        }
    
    def analyze_image(self, image_source: Union[str, bytes, Path], 
                     analysis_type: str = "auto") -> Dict:
        """
        General image analysis - auto-detects content type.
        """
        start_time = datetime.now()
        
        results = {
            "timestamp": start_time.isoformat(),
            "analysis_type": analysis_type,
            "text": None,
            "qr_codes": None,
            "summary": None
        }
        
        # Extract text
        ocr_result = self.extract_text(image_source)
        results["text"] = ocr_result
        
        # Check for QR codes
        qr_result = self.decode_qr(image_source)
        results["qr_codes"] = qr_result
        
        # Generate summary
        summary_parts = []
        
        if ocr_result.get("text"):
            word_count = ocr_result.get("word_count", 0)
            summary_parts.append(f"Found {word_count} words of text")
        
        if qr_result.get("codes"):
            summary_parts.append(f"Found {len(qr_result['codes'])} QR/barcode(s)")
            for code in qr_result["codes"]:
                summary_parts.append(f"  - {code['type']}: {code['data'][:50]}...")
        
        results["summary"] = "\n".join(summary_parts) if summary_parts else "No content detected"
        
        # Calculate processing time
        processing_time = (datetime.now() - start_time).total_seconds() * 1000
        results["processing_time_ms"] = processing_time
        
        # Store analysis
        conn = sqlite3.connect(self.db_path)
        conn.execute('''INSERT INTO image_analyses 
            (timestamp, analysis_type, extracted_text, qr_data, summary, processing_time_ms)
            VALUES (?, ?, ?, ?, ?, ?)''',
            (start_time.isoformat(), analysis_type,
             ocr_result.get("text", ""),
             json.dumps(qr_result.get("codes", [])),
             results["summary"], processing_time))
        conn.commit()
        conn.close()
        
        self.analysis_count += 1
        
        return results
    
    def analyze_screenshot(self, image_source: Union[str, bytes, Path]) -> Dict:
        """
        Analyze a screenshot - optimized for code, errors, UI elements.
        """
        result = self.analyze_image(image_source, "screenshot")
        
        text = result.get("text", {}).get("text", "")
        
        # Detect code patterns
        code_indicators = ["def ", "class ", "import ", "function", "const ", "let ", 
                         "var ", "if (", "for (", "while ", "return ", "=>", "->"]
        is_code = any(ind in text for ind in code_indicators)
        
        # Detect error patterns
        error_indicators = ["Error", "Exception", "Traceback", "failed", "error:", 
                          "warning:", "FAILED", "undefined", "null"]
        has_errors = any(ind in text for ind in error_indicators)
        
        result["content_analysis"] = {
            "likely_code": is_code,
            "has_errors": has_errors,
            "content_type": "code" if is_code else "error" if has_errors else "general"
        }
        
        return result
    
    def get_stats(self) -> Dict:
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("SELECT COUNT(*) FROM image_analyses")
        total = cursor.fetchone()[0]
        
        cursor.execute("SELECT COUNT(*) FROM business_cards")
        cards = cursor.fetchone()[0]
        
        cursor.execute("SELECT analysis_type, COUNT(*) FROM image_analyses GROUP BY analysis_type")
        by_type = {row[0]: row[1] for row in cursor.fetchall()}
        
        conn.close()
        
        return {
            "total_analyses": total,
            "business_cards": cards,
            "by_type": by_type,
            "capabilities": self.capabilities
        }


# Global instance
image_analysis = ImageAnalysis()


if __name__ == "__main__":
    print("\n" + "="*60)
    print("📷 EDEN IMAGE ANALYSIS SYSTEM")
    print("="*60)
    
    ia = image_analysis
    
    # Show capabilities
    print(f"\n📊 Capabilities:")
    for cap, available in ia.capabilities.items():
        status = "✅" if available else "❌"
        print(f"   {status} {cap}")
    
    # Test with a sample if available
    test_images = [
        "/Eden/DATA/test_image.png",
        "/Eden/DATA/test_card.jpg",
        "/tmp/test.png"
    ]
    
    for test_path in test_images:
        if Path(test_path).exists():
            print(f"\n🔍 Analyzing: {test_path}")
            result = ia.analyze_image(test_path)
            print(f"   Summary: {result['summary']}")
            break
    else:
        print(f"\n📝 No test images found. System ready for use.")
        print(f"\nUsage:")
        print(f"   from eden_image_analysis import image_analysis")
        print(f"   result = image_analysis.analyze_image('/path/to/image.png')")
        print(f"   result = image_analysis.analyze_business_card('/path/to/card.jpg')")
        print(f"   result = image_analysis.decode_qr('/path/to/qr.png')")
    
    # Stats
    stats = ia.get_stats()
    print(f"\n📊 Stats:")
    print(f"   Total analyses: {stats['total_analyses']}")
    print(f"   Business cards: {stats['business_cards']}")
