#!/usr/bin/env python3
"""
Auto-tag an image using vision model.
Extracts structured tags from taxonomy.json and saves to Notion.

Usage:
    python tag_image.py --url "https://example.com/image.jpg" --name "Reference Name"
    python tag_image.py --url "https://example.com/image.jpg" --name "Reference Name" --save
"""

import argparse
import json
import os
import sys
import base64
import requests
from pathlib import Path

# Paths
SCRIPT_DIR = Path(__file__).parent
TAXONOMY_PATH = SCRIPT_DIR / "taxonomy.json"
NOTION_KEY_PATH = Path("/home/clawd/secrets/notion/api_key")
GEMINI_KEY = "AIzaSyDXqYZInk83iVV4mD29pSuHQKbgkiI1x9Q"
INSPIRATION_DB_ID = "2ff330c2-8646-81f0-bbd9-ec474393d7a5"

def load_taxonomy():
    """Load the taxonomy from JSON."""
    with open(TAXONOMY_PATH) as f:
        return json.load(f)

def load_notion_key():
    """Load Notion API key."""
    with open(NOTION_KEY_PATH) as f:
        return f.read().strip()

def fetch_image_base64(url: str) -> tuple[str, str]:
    """Fetch image and return base64 + mime type."""
    resp = requests.get(url, timeout=30)
    resp.raise_for_status()
    content_type = resp.headers.get('content-type', 'image/jpeg')
    if 'jpeg' in content_type or 'jpg' in content_type:
        mime = 'image/jpeg'
    elif 'png' in content_type:
        mime = 'image/png'
    elif 'webp' in content_type:
        mime = 'image/webp'
    elif 'gif' in content_type:
        mime = 'image/gif'
    else:
        mime = 'image/jpeg'
    return base64.b64encode(resp.content).decode('utf-8'), mime

def analyze_with_gemini(image_url: str, taxonomy: dict) -> dict:
    """Use Gemini vision to analyze image and extract tags."""
    
    # Build the taxonomy prompt (exclude metadata fields starting with _ or version/updated)
    taxonomy_str = json.dumps({k: v for k, v in taxonomy.items() if not k.startswith('_') and k not in ['version', 'updated']}, indent=2)
    
    prompt = f"""Analyze this image as a design reference. Extract tags from ONLY the following taxonomy categories.

TAXONOMY (use ONLY these values):
{taxonomy_str}

Return JSON with TWO sections:

SECTION 1 - VISUAL TAGS:
- element_type: ONE value from the element_type list
- style: UP TO 3 values from the style list
- mood: UP TO 2 values from the mood list  
- color_family: UP TO 2 values from the color_family list
- subject: ONE value from the subject list
- content: UP TO 3 values from the content list (what's literally in the image)
- context: ONE value from the context list (setting/environment)
- action: ONE value from the action list (state of what's shown)
- craft: UP TO 2 values from the craft list (if applicable, otherwise empty array)
- descriptors: 3-5 FREE-FORM words that describe what's in this image

SECTION 2 - BRAND CONTEXT (use your knowledge of the brand/company shown):
- brand_name: The brand shown (e.g., "Sweetgreen", "Spotify") - extract from image or use provided name
- industry: ONE value from the industry list
- audience_type: ONE value from the audience_type list  
- market_position: ONE value from the market_position list
- brief_fit: UP TO 3 values from the brief_fit list (what kind of projects this reference suits)
- cultural_context: ONE sentence about why this brand matters (founder story, market significance, cultural relevance)

SECTION 3 - POSITIONING INTELLIGENCE (how this brand competes):
- positioning_strategy: UP TO 2 values from positioning_strategy list (HOW they differentiate)
- market_contrast: ONE value from market_contrast list (WHO they're positioned against)
- positioned_against: FREE-FORM string naming specific competitors (e.g., "McDonald's, Burger King" or "Aetna, BlueCross, UnitedHealth")
- positioning_narrative: ONE sentence explaining their market stance and how they achieve it visually/experientially
- affinity_keywords: 3-5 FREE-FORM searchable terms for brief matching (e.g., "rebellious", "elevated", "craft", "transparent")

SECTION 4 - SUMMARY:
- why_notable: ONE sentence explaining what makes this worth referencing as design inspiration

CRITICAL: Return a FLAT JSON object with all fields at the top level (not nested under VISUAL/CONTEXT/SUMMARY). Use only taxonomy values for tagged fields. Return valid JSON only, no markdown."""

    # Fetch and encode image
    try:
        image_b64, mime_type = fetch_image_base64(image_url)
    except Exception as e:
        print(f"Error fetching image: {e}", file=sys.stderr)
        sys.exit(1)

    # Call Gemini API
    url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={GEMINI_KEY}"
    
    payload = {
        "contents": [{
            "parts": [
                {
                    "inline_data": {
                        "mime_type": mime_type,
                        "data": image_b64
                    }
                },
                {"text": prompt}
            ]
        }],
        "generationConfig": {
            "temperature": 0.2,
            "maxOutputTokens": 4096
        }
    }
    
    resp = requests.post(url, json=payload, timeout=60)
    resp.raise_for_status()
    
    result = resp.json()
    
    # Extract text from response
    try:
        text = result['candidates'][0]['content']['parts'][0]['text']
        # Clean up markdown code blocks if present
        if '```json' in text:
            text = text.split('```json')[1].split('```')[0]
        elif '```' in text:
            text = text.split('```')[1].split('```')[0]
        parsed = json.loads(text.strip())
        
        # Flatten nested structure (VISUAL, CONTEXT, SUMMARY) into single dict
        flat = {}
        if 'VISUAL' in parsed:
            flat.update(parsed['VISUAL'])
        if 'CONTEXT' in parsed:
            flat.update(parsed['CONTEXT'])
        if 'SUMMARY' in parsed:
            flat.update(parsed['SUMMARY'])
        # If already flat, use as-is
        if not flat:
            flat = parsed
        return flat
    except (KeyError, IndexError, json.JSONDecodeError) as e:
        print(f"Error parsing Gemini response: {e}", file=sys.stderr)
        print(f"Raw response: {result}", file=sys.stderr)
        sys.exit(1)

def tags_to_notion_format(analysis: dict) -> list[dict]:
    """Convert analysis to Notion multi-select format."""
    tags = []
    
    # Helper to add tags from a field
    def add_tags(field_name):
        if field_name in analysis and analysis[field_name]:
            values = analysis[field_name] if isinstance(analysis[field_name], list) else [analysis[field_name]]
            for v in values:
                if v:  # skip empty strings
                    tags.append({"name": v})
    
    # Visual layer tags
    add_tags('element_type')
    add_tags('style')
    add_tags('mood')
    add_tags('color_family')
    add_tags('subject')
    add_tags('content')
    add_tags('context')
    add_tags('action')
    add_tags('craft')
    add_tags('descriptors')  # free-form evolving tags
    
    # Context layer tags (prefixed for clarity)
    add_tags('industry')
    add_tags('audience_type')
    add_tags('market_position')
    add_tags('brief_fit')
    
    # Positioning layer tags
    add_tags('positioning_strategy')
    add_tags('market_contrast')
    add_tags('affinity_keywords')  # free-form searchable terms
    
    return tags

def print_analysis(analysis: dict):
    """Pretty-print the analysis with visual and context layers separated."""
    print("\n" + "="*50)
    print("VISUAL LAYER")
    print("="*50)
    visual_fields = ['element_type', 'style', 'mood', 'color_family', 'subject', 
                     'content', 'context', 'action', 'craft', 'descriptors']
    for f in visual_fields:
        if f in analysis:
            print(f"  {f}: {analysis[f]}")
    
    print("\n" + "="*50)
    print("CONTEXT LAYER (Brand Intelligence)")
    print("="*50)
    if 'brand_name' in analysis:
        print(f"  Brand: {analysis['brand_name']}")
    context_fields = ['industry', 'audience_type', 'market_position', 'brief_fit']
    for f in context_fields:
        if f in analysis:
            print(f"  {f}: {analysis[f]}")
    if 'cultural_context' in analysis:
        print(f"  Cultural significance: {analysis['cultural_context']}")
    
    print("\n" + "="*50)
    print("POSITIONING INTELLIGENCE (Market Stance)")
    print("="*50)
    if 'positioning_strategy' in analysis:
        print(f"  Strategy: {analysis['positioning_strategy']}")
    if 'market_contrast' in analysis:
        print(f"  Positioned: {analysis['market_contrast']}")
    if 'positioned_against' in analysis:
        print(f"  Against: {analysis['positioned_against']}")
    if 'positioning_narrative' in analysis:
        print(f"  Narrative: {analysis['positioning_narrative']}")
    if 'affinity_keywords' in analysis:
        print(f"  🔍 Affinity keywords: {analysis['affinity_keywords']}")
    
    print("\n" + "="*50)
    print("SUMMARY")
    print("="*50)
    if 'why_notable' in analysis:
        print(f"  {analysis['why_notable']}")

def save_to_notion(name: str, url: str, tags: list[dict], why_notable: str):
    """Create a new entry in the Inspiration Library."""
    notion_key = load_notion_key()
    
    api_url = "https://api.notion.com/v1/pages"
    headers = {
        "Authorization": f"Bearer {notion_key}",
        "Content-Type": "application/json",
        "Notion-Version": "2022-06-28"
    }
    
    payload = {
        "parent": {"database_id": INSPIRATION_DB_ID},
        "properties": {
            "Name": {
                "title": [{"text": {"content": name}}]
            },
            "Link": {
                "url": url
            },
            "Tags": {
                "multi_select": tags
            }
        }
    }
    
    resp = requests.post(api_url, headers=headers, json=payload, timeout=30)
    resp.raise_for_status()
    return resp.json()

def main():
    parser = argparse.ArgumentParser(description="Auto-tag an image using vision AI")
    parser.add_argument("--url", required=True, help="Image URL to analyze")
    parser.add_argument("--name", required=True, help="Name for the reference")
    parser.add_argument("--save", action="store_true", help="Save to Notion (default: dry run)")
    args = parser.parse_args()
    
    print(f"Loading taxonomy...")
    taxonomy = load_taxonomy()
    
    print(f"Analyzing image: {args.url}")
    analysis = analyze_with_gemini(args.url, taxonomy)
    
    # Pretty-print analysis with layers
    print_analysis(analysis)
    
    tags = tags_to_notion_format(analysis)
    print(f"\n=== Tags for Notion ===")
    print([t['name'] for t in tags])
    
    if args.save:
        print(f"\nSaving to Notion...")
        result = save_to_notion(
            name=args.name,
            url=args.url,
            tags=tags,
            why_notable=analysis.get('why_notable', '')
        )
        print(f"✅ Saved! Page ID: {result['id']}")
    else:
        print(f"\n[DRY RUN] Would save to Notion with name: {args.name}")
        print("Run with --save to actually save.")

if __name__ == "__main__":
    main()
