#!/usr/bin/env python3
"""
SVG Review Server v2
Single-image review flow with comments, variations, and idea capture.
"""

import http.server
import json
import os
import shutil
import urllib.parse
from pathlib import Path
from datetime import datetime

PORT = 8766  # Permanent port - bookmark this!
BASE_DIR = Path.home() / "Documents" / "Etsy Designs"
REVIEW_DIR = BASE_DIR / "02-SVG-Review"
READY_DIR = BASE_DIR / "03-SVG-Ready"
REJECTED_DIR = BASE_DIR / "04-Archive" / "rejected"
WORKSPACE = Path.home() / ".openclaw" / "workspace"
IDEAS_FILE = WORKSPACE / "data" / "svg-ideas.json"
FEEDBACK_FILE = WORKSPACE / "data" / "svg-feedback.json"

class ReviewHandler(http.server.BaseHTTPRequestHandler):
    
    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        path = parsed.path
        
        if path == "/" or path == "/review":
            self.serve_review_page()
        elif path.startswith("/svg/"):
            self.serve_svg(path[5:])
        elif path == "/api/list":
            self.api_list()
        elif path == "/api/current":
            self.api_current()
        elif path == "/api/ideas":
            self.api_get_ideas()
        elif path == "/api/spend":
            self.api_spend()
        else:
            self.send_error(404)
    
    def do_POST(self):
        parsed = urllib.parse.urlparse(self.path)
        content_length = int(self.headers.get("Content-Length", 0))
        body = self.rfile.read(content_length).decode() if content_length > 0 else "{}"
        
        try:
            data = json.loads(body) if body else {}
        except:
            data = {}
        
        if parsed.path == "/api/approve":
            self.api_action("approve", data)
        elif parsed.path == "/api/reject":
            self.api_action("reject", data)
        elif parsed.path == "/api/idea":
            self.api_save_idea(data)
        else:
            self.send_error(404)
    
    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()
    
    def get_files(self):
        files = []
        if REVIEW_DIR.exists():
            for f in sorted(REVIEW_DIR.glob("*.svg")):
                files.append({
                    "name": f.name,
                    "path": str(f),
                    "size": f"{f.stat().st_size // 1024} KB"
                })
        return files
    
    def serve_svg(self, filename):
        filepath = REVIEW_DIR / filename
        if filepath.exists() and filepath.suffix.lower() == ".svg":
            with open(filepath, "rb") as f:
                content = f.read()
            self.send_response(200)
            self.send_header("Content-Type", "image/svg+xml")
            self.send_header("Content-Length", len(content))
            self.send_header("Cache-Control", "no-cache")
            self.end_headers()
            self.wfile.write(content)
        else:
            self.send_error(404)
    
    def api_list(self):
        files = self.get_files()
        self.send_json({"files": files, "count": len(files)})
    
    def api_current(self):
        files = self.get_files()
        if files:
            self.send_json({"file": files[0], "remaining": len(files)})
        else:
            self.send_json({"file": None, "remaining": 0})
    
    def api_spend(self):
        spend_file = WORKSPACE / "data" / "dalle-spend.json"
        if spend_file.exists():
            with open(spend_file) as f:
                self.send_json(json.load(f))
        else:
            self.send_json({"totalSpend": 0, "totalImages": 0})
    
    def api_action(self, action, data):
        filename = data.get("filename", "")
        comment = data.get("comment", "").strip()
        request_variation = data.get("variation", False)
        
        if not filename:
            self.send_json({"error": "No filename"}, 400)
            return
        
        src = REVIEW_DIR / filename
        if not src.exists():
            self.send_json({"error": f"File not found: {filename}"}, 404)
            return
        
        # Determine destination
        dest_dir = READY_DIR if action == "approve" else REJECTED_DIR
        dest_dir.mkdir(parents=True, exist_ok=True)
        dest = dest_dir / filename
        
        # Handle duplicates
        counter = 1
        while dest.exists():
            dest = dest_dir / f"{src.stem}-{counter}{src.suffix}"
            counter += 1
        
        shutil.move(str(src), str(dest))
        
        # Save feedback if any
        if comment or request_variation:
            self.save_feedback(filename, action, comment, request_variation)
        
        # Get next file
        files = self.get_files()
        next_file = files[0] if files else None
        
        self.send_json({
            "success": True,
            "action": action,
            "filename": filename,
            "destination": str(dest),
            "next": next_file,
            "remaining": len(files)
        })
    
    def save_feedback(self, filename, action, comment, variation):
        FEEDBACK_FILE.parent.mkdir(parents=True, exist_ok=True)
        feedback = []
        if FEEDBACK_FILE.exists():
            with open(FEEDBACK_FILE) as f:
                feedback = json.load(f)
        
        feedback.append({
            "timestamp": datetime.now().isoformat(),
            "filename": filename,
            "action": action,
            "comment": comment,
            "requestVariation": variation
        })
        
        with open(FEEDBACK_FILE, 'w') as f:
            json.dump(feedback, f, indent=2)
    
    def api_save_idea(self, data):
        idea = data.get("idea", "").strip()
        if not idea:
            self.send_json({"error": "No idea provided"}, 400)
            return
        
        IDEAS_FILE.parent.mkdir(parents=True, exist_ok=True)
        ideas = []
        if IDEAS_FILE.exists():
            with open(IDEAS_FILE) as f:
                ideas = json.load(f)
        
        ideas.append({
            "timestamp": datetime.now().isoformat(),
            "idea": idea,
            "status": "pending"
        })
        
        with open(IDEAS_FILE, 'w') as f:
            json.dump(ideas, f, indent=2)
        
        self.send_json({"success": True, "totalIdeas": len(ideas)})
    
    def api_get_ideas(self):
        if IDEAS_FILE.exists():
            with open(IDEAS_FILE) as f:
                ideas = json.load(f)
            pending = [i for i in ideas if i.get("status") == "pending"]
            self.send_json({"ideas": pending, "total": len(ideas)})
        else:
            self.send_json({"ideas": [], "total": 0})
    
    def send_json(self, data, status=200):
        body = json.dumps(data).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", len(body))
        self.send_header("Access-Control-Allow-Origin", "*")
        self.end_headers()
        self.wfile.write(body)
    
    def serve_review_page(self):
        files = self.get_files()
        current = files[0] if files else None
        ready_count = len(list(READY_DIR.glob("*.svg"))) if READY_DIR.exists() else 0
        
        # Get spend
        spend_file = WORKSPACE / "data" / "dalle-spend.json"
        today_spend = month_spend = 0
        if spend_file.exists():
            with open(spend_file) as f:
                spend = json.load(f)
            today = datetime.now().strftime("%Y-%m-%d")
            month = datetime.now().strftime("%Y-%m")
            today_spend = spend.get("dailySpend", {}).get(today, {}).get("spend", 0)
            month_spend = spend.get("monthlySpend", {}).get(month, {}).get("spend", 0)
        
        html = f'''<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SVG Review — Royal Styles Creations</title>
  <style>
    :root {{
      --bg: #1a1a2e;
      --card-bg: #16213e;
      --accent: #e94560;
      --success: #27ae60;
      --text: #eee;
      --text-dim: #888;
      --border: #0f3460;
    }}
    * {{ box-sizing: border-box; margin: 0; padding: 0; }}
    body {{
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
    }}
    header {{
      background: var(--card-bg);
      padding: 1rem 2rem;
      border-bottom: 1px solid var(--border);
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-wrap: wrap;
      gap: 1rem;
    }}
    header h1 {{ font-size: 1.4rem; }}
    .stats {{
      display: flex;
      gap: 1.5rem;
      font-size: 0.9rem;
    }}
    .stats span {{ color: var(--accent); font-weight: 600; }}
    
    .container {{
      flex: 1;
      display: flex;
      gap: 1.5rem;
      padding: 1.5rem;
      max-width: 1400px;
      margin: 0 auto;
      width: 100%;
    }}
    
    .preview-section {{
      flex: 2;
      background: var(--card-bg);
      border-radius: 12px;
      border: 1px solid var(--border);
      display: flex;
      flex-direction: column;
    }}
    .preview-header {{
      padding: 1rem;
      border-bottom: 1px solid var(--border);
      display: flex;
      justify-content: space-between;
      align-items: center;
    }}
    .preview-header h2 {{
      font-size: 1rem;
      color: var(--text-dim);
      font-weight: normal;
    }}
    .preview-header .filename {{
      color: var(--accent);
      font-weight: 600;
    }}
    .preview-area {{
      flex: 1;
      background: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 2rem;
      min-height: 400px;
    }}
    .preview-area img {{
      max-width: 100%;
      max-height: 500px;
      object-fit: contain;
    }}
    .preview-actions {{
      padding: 1.5rem;
      border-top: 1px solid var(--border);
    }}
    .action-row {{
      display: flex;
      gap: 1rem;
      margin-bottom: 1rem;
    }}
    .btn {{
      padding: 0.8rem 1.5rem;
      border: none;
      border-radius: 8px;
      font-weight: 600;
      font-size: 1rem;
      cursor: pointer;
      transition: all 0.2s;
      flex: 1;
    }}
    .btn:hover {{ transform: translateY(-2px); opacity: 0.9; }}
    .btn:disabled {{ opacity: 0.5; cursor: not-allowed; transform: none; }}
    .btn-approve {{ background: var(--success); color: #fff; }}
    .btn-reject {{ background: var(--accent); color: #fff; }}
    
    .comment-section {{
      margin-top: 0.5rem;
    }}
    .comment-section label {{
      display: block;
      font-size: 0.85rem;
      color: var(--text-dim);
      margin-bottom: 0.5rem;
    }}
    .comment-section textarea {{
      width: 100%;
      padding: 0.75rem;
      border-radius: 8px;
      border: 1px solid var(--border);
      background: var(--bg);
      color: var(--text);
      font-family: inherit;
      font-size: 0.9rem;
      resize: vertical;
      min-height: 60px;
    }}
    .comment-section textarea:focus {{
      outline: none;
      border-color: var(--accent);
    }}
    .variation-check {{
      margin-top: 0.75rem;
      display: flex;
      align-items: center;
      gap: 0.5rem;
      font-size: 0.9rem;
    }}
    .variation-check input {{
      width: 18px;
      height: 18px;
      cursor: pointer;
    }}
    
    .side-panel {{
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 1rem;
      min-width: 280px;
    }}
    .panel {{
      background: var(--card-bg);
      border-radius: 12px;
      border: 1px solid var(--border);
      padding: 1rem;
    }}
    .panel h3 {{
      font-size: 0.95rem;
      margin-bottom: 0.75rem;
      color: var(--accent);
    }}
    .panel textarea {{
      width: 100%;
      padding: 0.75rem;
      border-radius: 8px;
      border: 1px solid var(--border);
      background: var(--bg);
      color: var(--text);
      font-family: inherit;
      font-size: 0.9rem;
      resize: vertical;
      min-height: 80px;
    }}
    .panel textarea:focus {{
      outline: none;
      border-color: var(--accent);
    }}
    .panel .btn-small {{
      margin-top: 0.5rem;
      padding: 0.5rem 1rem;
      font-size: 0.85rem;
      background: var(--border);
      color: var(--text);
      border: none;
      border-radius: 6px;
      cursor: pointer;
      width: 100%;
    }}
    .panel .btn-small:hover {{ background: var(--accent); }}
    
    .queue-list {{
      max-height: 200px;
      overflow-y: auto;
      font-size: 0.85rem;
    }}
    .queue-item {{
      padding: 0.5rem;
      border-bottom: 1px solid var(--border);
      color: var(--text-dim);
    }}
    .queue-item:last-child {{ border-bottom: none; }}
    
    .empty-state {{
      flex: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 3rem;
      text-align: center;
    }}
    .empty-state h2 {{
      color: var(--success);
      font-size: 2rem;
      margin-bottom: 1rem;
    }}
    .empty-state p {{
      color: var(--text-dim);
      font-size: 1.1rem;
    }}
    
    .toast {{
      position: fixed;
      bottom: 2rem;
      left: 50%;
      transform: translateX(-50%) translateY(100px);
      background: var(--success);
      color: white;
      padding: 1rem 2rem;
      border-radius: 8px;
      font-weight: 600;
      opacity: 0;
      transition: all 0.3s;
      z-index: 1000;
    }}
    .toast.show {{ opacity: 1; transform: translateX(-50%) translateY(0); }}
    .toast.error {{ background: var(--accent); }}
  </style>
</head>
<body>
  <header>
    <h1>🎨 SVG Review</h1>
    <div class="stats">
      <div>Remaining: <span id="remaining">{len(files)}</span></div>
      <div>Ready: <span id="ready">{ready_count}</span></div>
      <div>DALL-E Today: <span>${today_spend:.2f}</span></div>
      <div>Month: <span>${month_spend:.2f}</span></div>
    </div>
  </header>

  <div class="container" id="main-container">
    {'<div class="empty-state"><h2>✓ All Done!</h2><p>No designs waiting for review.</p></div>' if not current else f"""
    <div class="preview-section">
      <div class="preview-header">
        <h2>Reviewing: <span class="filename" id="current-filename">{current['name']}</span></h2>
        <span id="file-size">{current['size']}</span>
      </div>
      <div class="preview-area">
        <img id="preview-img" src="/svg/{current['name']}" alt="Preview">
      </div>
      <div class="preview-actions">
        <div class="action-row">
          <button class="btn btn-approve" onclick="approve()">✓ Approve</button>
          <button class="btn btn-reject" onclick="reject()">✗ Reject</button>
        </div>
        <div class="comment-section">
          <label for="comment">Feedback / Instructions for Tony:</label>
          <textarea id="comment" placeholder="Optional: describe issues, request changes, etc."></textarea>
          <div class="variation-check">
            <input type="checkbox" id="variation">
            <label for="variation">Request a variation of this design</label>
          </div>
        </div>
      </div>
    </div>
    
    <div class="side-panel">
      <div class="panel">
        <h3>💡 New Idea</h3>
        <textarea id="new-idea" placeholder="Jot down a design idea while reviewing..."></textarea>
        <button class="btn-small" onclick="saveIdea()">Save Idea</button>
      </div>
      
      <div class="panel">
        <h3 id="queue-header">📋 Queue ({len(files)} remaining)</h3>
        <div class="queue-list" id="queue-list">
          {"".join(f'<div class="queue-item">{f["name"]}</div>' for f in files[:10])}
          {f'<div class="queue-item">...and {len(files) - 10} more</div>' if len(files) > 10 else ''}
        </div>
      </div>
    </div>
    """}
  </div>
  
  <div id="toast" class="toast"></div>

  <script>
    let currentFile = {json.dumps(current['name']) if current else 'null'};
    
    function showToast(message, isError = false) {{
      const toast = document.getElementById('toast');
      toast.textContent = message;
      toast.className = 'toast show' + (isError ? ' error' : '');
      setTimeout(() => toast.className = 'toast', 2500);
    }}
    
    async function doAction(action) {{
      if (!currentFile) return;
      
      const comment = document.getElementById('comment')?.value || '';
      const variation = document.getElementById('variation')?.checked || false;
      
      try {{
        const resp = await fetch('/api/' + action, {{
          method: 'POST',
          headers: {{ 'Content-Type': 'application/json' }},
          body: JSON.stringify({{
            filename: currentFile,
            comment: comment,
            variation: variation
          }})
        }});
        const data = await resp.json();
        
        if (data.success) {{
          showToast((action === 'approve' ? '✓ Approved: ' : '✗ Rejected: ') + currentFile);
          
          // Update counts
          document.getElementById('remaining').textContent = data.remaining;
          if (action === 'approve') {{
            const ready = document.getElementById('ready');
            ready.textContent = parseInt(ready.textContent) + 1;
          }}
          
          // Load next or show empty
          if (data.next) {{
            currentFile = data.next.name;
            document.getElementById('current-filename').textContent = data.next.name;
            document.getElementById('file-size').textContent = data.next.size;
            document.getElementById('preview-img').src = '/svg/' + data.next.name + '?' + Date.now();
            document.getElementById('comment').value = '';
            document.getElementById('variation').checked = false;
            updateQueue(data.remaining);
          }} else {{
            document.getElementById('main-container').innerHTML = '<div class="empty-state"><h2>✓ All Done!</h2><p>No designs waiting for review.</p></div>';
          }}
        }} else {{
          showToast('Error: ' + data.error, true);
        }}
      }} catch (e) {{
        showToast('Error: ' + e.message, true);
      }}
    }}
    
    function approve() {{ doAction('approve'); }}
    function reject() {{ doAction('reject'); }}
    
    async function updateQueue(remaining) {{
      try {{
        const resp = await fetch('/api/list');
        const data = await resp.json();
        const list = document.getElementById('queue-list');
        const header = document.getElementById('queue-header');
        if (header) {{
          header.textContent = `📋 Queue (${{data.count}} remaining)`;
        }}
        if (list && data.files) {{
          let html = data.files.slice(0, 10).map(f => `<div class="queue-item">${{f.name}}</div>`).join('');
          if (data.files.length > 10) {{
            html += `<div class="queue-item">...and ${{data.files.length - 10}} more</div>`;
          }}
          list.innerHTML = html || '<div class="queue-item" style="color: var(--success);">Queue empty!</div>';
        }}
      }} catch (e) {{}}
    }}
    
    async function saveIdea() {{
      const ideaEl = document.getElementById('new-idea');
      const idea = ideaEl.value.trim();
      if (!idea) {{
        showToast('Enter an idea first', true);
        return;
      }}
      
      try {{
        const resp = await fetch('/api/idea', {{
          method: 'POST',
          headers: {{ 'Content-Type': 'application/json' }},
          body: JSON.stringify({{ idea }})
        }});
        const data = await resp.json();
        
        if (data.success) {{
          showToast('💡 Idea saved! (' + data.totalIdeas + ' total)');
          ideaEl.value = '';
        }} else {{
          showToast('Error: ' + data.error, true);
        }}
      }} catch (e) {{
        showToast('Error: ' + e.message, true);
      }}
    }}
    
    // Keyboard shortcuts
    document.addEventListener('keydown', (e) => {{
      if (e.target.tagName === 'TEXTAREA') return;
      if (e.key === 'a' || e.key === 'A') approve();
      if (e.key === 'r' || e.key === 'R' || e.key === 'x' || e.key === 'X') reject();
    }});
  </script>
</body>
</html>'''
        
        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.send_header("Content-Length", len(html.encode()))
        self.end_headers()
        self.wfile.write(html.encode())
    
    def log_message(self, format, *args):
        pass


import socket

class ReusableTCPServer(http.server.HTTPServer):
    allow_reuse_address = True
    
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        super().server_bind()

def main():
    REVIEW_DIR.mkdir(parents=True, exist_ok=True)
    READY_DIR.mkdir(parents=True, exist_ok=True)
    REJECTED_DIR.mkdir(parents=True, exist_ok=True)
    
    server = ReusableTCPServer(("", PORT), ReviewHandler)
    print(f"🎨 SVG Review Server v2 running at http://localhost:{PORT}")
    print(f"   Keyboard: A=Approve, R/X=Reject")
    print(f"   Press Ctrl+C to stop")
    
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("\n✓ Server stopped")


if __name__ == "__main__":
    main()

# TONY-APPROVED: 2026-03-01 | sha:dff2e5e3

# TONY-APPROVED: 2026-03-01 | sha:fc607b44
