#!/usr/bin/env python3
"""
SVG Batch 1 Fixer — Automated production readiness fixes.

For each SVG:
1. Apply design-specific fixes (enlarge subtitles, remove problem elements, 
   fix opacity, thicken lines, simplify borders)
2. Run Inkscape to convert text→paths
3. Clean up Inkscape namespace cruft
4. Save to svg-batch-1-fixed/

Tony — Feb 4, 2026 nightly work session
"""

import os
import sys
import re
import subprocess
import xml.etree.ElementTree as ET
import copy
import shutil

SRC_DIR = "/Users/tonyclaw/.openclaw/workspace/drafts/svg-batch-1"
DST_DIR = "/Users/tonyclaw/.openclaw/workspace/drafts/svg-batch-1-fixed"
TEMP_DIR = "/tmp/svg-fix-temp"

# Track what we did
fixes_log = {}

def ensure_dirs():
    os.makedirs(DST_DIR, exist_ok=True)
    os.makedirs(TEMP_DIR, exist_ok=True)

def get_svg_files():
    """Get all SVG files from batch 1."""
    files = []
    for f in sorted(os.listdir(SRC_DIR)):
        if f.endswith('.svg'):
            files.append(f)
    return files

def read_svg(filepath):
    """Read SVG as string."""
    with open(filepath, 'r') as f:
        return f.read()

def write_svg(filepath, content):
    """Write SVG string to file."""
    with open(filepath, 'w') as f:
        f.write(content)

# ============================================================
# Fix functions — operate on raw SVG text for simplicity
# (XML parsers mess up SVG attributes and namespaces)
# ============================================================

def fix_subtitle_size(svg_text, filename):
    """Increase subtitle text from font-size='20' to font-size='28'."""
    count = 0
    # Match the subtitle text elements (font-size="20" with letter-spacing="7")
    pattern = r'(font-size=")20(".*?letter-spacing="7")'
    if re.search(pattern, svg_text):
        svg_text = re.sub(pattern, r'\g<1>28\2', svg_text)
        count += 1
    # Also try the reverse order
    pattern2 = r'(letter-spacing="7".*?font-size=")20(")'
    if re.search(pattern2, svg_text):
        svg_text = re.sub(pattern2, r'\g<1>28\2', svg_text)
        count += 1
    if count > 0:
        fixes_log.setdefault(filename, []).append(f"Enlarged subtitle from 20pt to 28pt")
    return svg_text

def fix_thin_strokes(svg_text, filename):
    """Increase any stroke-width less than 3 to 3."""
    def replace_stroke(match):
        val = float(match.group(1))
        if val < 3:
            fixes_log.setdefault(filename, []).append(f"Thickened stroke from {val} to 3")
            return f'stroke-width="{3}"'
        return match.group(0)
    svg_text = re.sub(r'stroke-width="([0-9.]+)"', replace_stroke, svg_text)
    return svg_text

def fix_remove_opacity_groups(svg_text, filename):
    """Remove <g> groups with opacity < 0.5 (distress/texture marks won't cut)."""
    # Find groups with low opacity
    pattern = r'<g[^>]*opacity="(0\.[0-4]\d*)"[^>]*>.*?</g>'
    matches = re.findall(r'opacity="(0\.[0-4]\d*)"', svg_text)
    if matches:
        svg_text = re.sub(pattern, '', svg_text, flags=re.DOTALL)
        fixes_log.setdefault(filename, []).append(f"Removed {len(matches)} low-opacity groups (won't cut in vinyl)")
    return svg_text

def fix_enthusiasm(svg_text, filename):
    """Fix enthusiasm-specific issues."""
    fixes = []
    
    # Remove sunburst rays group (opacity="0.12")
    if 'opacity="0.12"' in svg_text:
        svg_text = re.sub(r'<!-- Sunburst rays.*?</g>', '', svg_text, flags=re.DOTALL)
        fixes.append("Removed sunburst rays (12% opacity won't cut)")
    
    # Remove small burst stars
    if '<!-- Burst stars' in svg_text:
        svg_text = re.sub(r'<!-- Burst stars.*?(?=\n\s*<!--|\n\s*<text)', '', svg_text, flags=re.DOTALL)
        fixes.append("Removed small decorative stars")
    
    # Remove exclamation accents
    if '<!-- Exclamation' in svg_text:
        svg_text = re.sub(r'<!-- Exclamation accents.*?(?=\n\s*<!--|\n\s*<text)', '', svg_text, flags=re.DOTALL)
        fixes.append("Removed exclamation mark accents")
    
    # Replace wavy divider with straight line
    if 'Wavy' in svg_text or 'wavy' in svg_text:
        svg_text = re.sub(
            r'<!-- Wavy.*?<path d="M200,240.*?/>',
            '<!-- Clean divider -->\n  <line x1="200" y1="240" x2="600" y2="240" stroke="#000" stroke-width="3"/>',
            svg_text, flags=re.DOTALL
        )
        fixes.append("Replaced wavy divider with clean straight line")
    
    # Remove bottom burst
    if '<!-- Bottom burst' in svg_text:
        svg_text = re.sub(r'<!-- Bottom burst.*?</g>', '', svg_text, flags=re.DOTALL)
        fixes.append("Removed bottom burst shape")
    
    if fixes:
        fixes_log.setdefault(filename, []).extend(fixes)
    return svg_text

def fix_distressed(svg_text, filename):
    """Fix semperfi-distressed — strip all distress effects, clean border."""
    fixes = []
    
    # Remove distress texture marks
    svg_text = re.sub(r'<!-- Distress texture.*?</g>', '', svg_text, flags=re.DOTALL)
    fixes.append("Removed all distress texture marks")
    
    # Replace irregular border with clean rectangle
    svg_text = re.sub(
        r'<!-- Rough border.*?<path d="M15,12.*?/>',
        '<!-- Clean border -->\n  <rect x="15" y="12" width="770" height="376" fill="none" stroke="#000" stroke-width="5"/>',
        svg_text, flags=re.DOTALL
    )
    fixes.append("Replaced irregular border with clean rectangle")
    
    # Remove scratchy line, replace with clean divider
    svg_text = re.sub(
        r'<!-- Rough scratchy.*?<path d="M130,210.*?/>',
        '<!-- Clean divider -->\n  <line x1="130" y1="210" x2="670" y2="210" stroke="#000" stroke-width="4"/>',
        svg_text, flags=re.DOTALL
    )
    fixes.append("Replaced scratchy divider with clean line")
    
    # Remove distress overlay marks
    svg_text = re.sub(r'<!-- Additional distress.*?</g>', '', svg_text, flags=re.DOTALL)
    fixes.append("Removed distress overlay marks")
    
    # Remove worn corner marks
    svg_text = re.sub(r'<!-- Worn corner.*?</g>', '', svg_text, flags=re.DOTALL)
    fixes.append("Removed worn corner marks")
    
    fixes_log.setdefault(filename, []).extend(fixes)
    return svg_text

def fix_nested_borders(svg_text, filename):
    """Simplify nested/double borders to single borders."""
    # Count rect elements that look like borders (near edges, fill="none")
    border_rects = re.findall(r'<rect[^>]*fill="none"[^>]*stroke[^>]*/>', svg_text)
    if len(border_rects) > 1:
        # Keep only the outermost border (first one)
        # This is a heuristic — works for most of our designs
        first_found = False
        def keep_first_border(match):
            nonlocal first_found
            if not first_found:
                first_found = True
                return match.group(0)
            fixes_log.setdefault(filename, []).append("Removed inner nested border")
            return f'<!-- removed nested border -->'
        svg_text = re.sub(r'<rect[^>]*fill="none"[^>]*stroke[^>]*/>', keep_first_border, svg_text)
    return svg_text

def fix_loyalty_multicolor(svg_text, filename):
    """Fix loyalty.svg — remove white-on-black elements that require multi-layer vinyl."""
    if 'loyalty' not in filename:
        return svg_text
    # Remove fill="white" or fill="#fff" or fill="#ffffff" elements  
    if 'fill="white"' in svg_text or 'fill="#fff"' in svg_text or 'fill="#ffffff"' in svg_text:
        svg_text = re.sub(r'fill="(white|#fff|#ffffff)"', 'fill="none"', svg_text)
        fixes_log.setdefault(filename, []).append("Converted white fills to transparent (single-color vinyl)")
    return svg_text

def fix_di_cover_gray(svg_text, filename):
    """Fix DI cover designs — remove gray/multi-color elements."""
    if 'di-cover' not in filename:
        return svg_text
    # Convert gray fills to black
    gray_pattern = r'fill="(#[89a-fA-F][0-9a-fA-F]{5}|gray|grey|#ccc|#ddd|#eee|#aaa|#bbb|#666|#777|#888|#999)"'
    if re.search(gray_pattern, svg_text):
        svg_text = re.sub(gray_pattern, 'fill="#000"', svg_text)
        fixes_log.setdefault(filename, []).append("Converted gray fills to black (single-color vinyl)")
    return svg_text

def inkscape_text_to_path(input_path, output_path):
    """Use Inkscape to convert all text to outlined paths."""
    result = subprocess.run(
        ['inkscape', input_path, 
         '--actions=select-all;object-to-path;export-filename:' + output_path + ';export-do'],
        capture_output=True, text=True, timeout=30
    )
    return result.returncode == 0

def clean_inkscape_output(svg_text):
    """Remove Inkscape-specific namespace cruft while keeping valid SVG."""
    # Remove sodipodi and inkscape namespace declarations and elements
    svg_text = re.sub(r'\s+xmlns:inkscape="[^"]*"', '', svg_text)
    svg_text = re.sub(r'\s+xmlns:sodipodi="[^"]*"', '', svg_text)
    svg_text = re.sub(r'\s+xmlns:svg="[^"]*"', '', svg_text)
    svg_text = re.sub(r'\s+sodipodi:[a-zA-Z]+="[^"]*"', '', svg_text)
    svg_text = re.sub(r'\s+inkscape:[a-zA-Z]+="[^"]*"', '', svg_text)
    svg_text = re.sub(r'<sodipodi:namedview[^/]*/>', '', svg_text)
    svg_text = re.sub(r'<sodipodi:namedview.*?/>', '', svg_text, flags=re.DOTALL)
    # Remove id attributes added by Inkscape
    svg_text = re.sub(r'\s+id="[^"]*"', '', svg_text)
    # Remove empty defs
    svg_text = re.sub(r'<defs\s*/>', '', svg_text)
    svg_text = re.sub(r'<defs>\s*</defs>', '', svg_text)
    # Remove standalone="no"
    svg_text = re.sub(r'\s+standalone="no"', '', svg_text)
    # Remove version="1.1"
    svg_text = re.sub(r'\s+version="1.1"', '', svg_text)
    # Clean up multiple blank lines
    svg_text = re.sub(r'\n{3,}', '\n\n', svg_text)
    return svg_text

def process_file(filename):
    """Process a single SVG file through all fixes."""
    src_path = os.path.join(SRC_DIR, filename)
    temp_pre_path = os.path.join(TEMP_DIR, f"pre_{filename}")
    temp_post_path = os.path.join(TEMP_DIR, f"post_{filename}")
    dst_path = os.path.join(DST_DIR, filename)
    
    print(f"\n{'='*60}")
    print(f"Processing: {filename}")
    print(f"{'='*60}")
    
    svg_text = read_svg(src_path)
    has_text = '<text' in svg_text
    
    # Apply design-specific fixes BEFORE text-to-path conversion
    # (so we can still match text elements and comments)
    
    # Universal fixes
    svg_text = fix_subtitle_size(svg_text, filename)
    svg_text = fix_remove_opacity_groups(svg_text, filename)
    svg_text = fix_thin_strokes(svg_text, filename)
    svg_text = fix_loyalty_multicolor(svg_text, filename)
    svg_text = fix_di_cover_gray(svg_text, filename)
    
    # File-specific fixes
    if filename == 'enthusiasm.svg':
        svg_text = fix_enthusiasm(svg_text, filename)
    elif filename == 'semperfi-distressed.svg':
        svg_text = fix_distressed(svg_text, filename)
    
    # Save pre-Inkscape version
    write_svg(temp_pre_path, svg_text)
    
    # Convert text to paths via Inkscape
    if has_text:
        print(f"  Converting text to paths via Inkscape...")
        success = inkscape_text_to_path(temp_pre_path, temp_post_path)
        if success and os.path.exists(temp_post_path):
            svg_text = read_svg(temp_post_path)
            svg_text = clean_inkscape_output(svg_text)
            fixes_log.setdefault(filename, []).append("Converted all text elements to outlined paths")
            
            # Verify no text elements remain
            if '<text' in svg_text:
                print(f"  ⚠️  WARNING: Text elements still present after conversion!")
                fixes_log[filename].append("WARNING: Some text may not have converted")
            else:
                print(f"  ✅ Text successfully converted to paths")
        else:
            print(f"  ❌ Inkscape conversion failed, using pre-fix version")
            fixes_log.setdefault(filename, []).append("ERROR: Inkscape text-to-path failed")
    else:
        print(f"  No text elements — skipping Inkscape conversion")
        fixes_log.setdefault(filename, []).append("No text elements (silhouette design)")
    
    # Save final version
    write_svg(dst_path, svg_text)
    
    # Report
    file_fixes = fixes_log.get(filename, [])
    for fix in file_fixes:
        print(f"  • {fix}")
    
    return True

def generate_report():
    """Generate a markdown report of all fixes applied."""
    report = []
    report.append("# SVG Batch 1 — Fix Report")
    report.append(f"**Generated:** February 4, 2026 (nightly work session)")
    report.append(f"**Total files processed:** {len(fixes_log)}")
    report.append("")
    report.append("## Summary")
    report.append("")
    
    # Count categories
    ready = []
    needs_design = []
    
    # Silhouettes and major-fail files
    fail_files = {'female-attention.svg', 'male-attention.svg', 'female-salute.svg', 'male-salute.svg',
                  'jjdidtiebuckle-combined.svg', 'semperfi-script.svg'}
    
    for filename, fixes in sorted(fixes_log.items()):
        has_error = any('ERROR' in f or 'WARNING' in f for f in fixes)
        if filename in fail_files:
            needs_design.append(filename)
        elif has_error:
            needs_design.append(filename)
        else:
            ready.append(filename)
    
    report.append(f"- **✅ Production-ready (after text-to-path):** {len(ready)} designs")
    report.append(f"- **⚠️ Still needs manual design work:** {len(needs_design)} designs")
    report.append("")
    
    report.append("## ✅ Production-Ready Designs")
    report.append("")
    report.append("These designs have been fixed and are ready for test cutting:")
    report.append("")
    for f in ready:
        report.append(f"### {f}")
        for fix in fixes_log[f]:
            report.append(f"- {fix}")
        report.append("")
    
    if needs_design:
        report.append("## ⚠️ Designs Needing Manual Work")
        report.append("")
        report.append("These designs have technical fixes applied but still need human design intervention:")
        report.append("")
        for f in needs_design:
            report.append(f"### {f}")
            for fix in fixes_log[f]:
                report.append(f"- {fix}")
            if f in {'female-attention.svg', 'male-attention.svg', 'female-salute.svg', 'male-salute.svg'}:
                report.append("- **NEEDS:** Professional silhouette redesign (current versions are stick-figure quality)")
            elif f == 'jjdidtiebuckle-combined.svg':
                report.append("- **NEEDS:** Redesign as poster/print format OR simplify to just the acronym")
            elif f == 'semperfi-script.svg':
                report.append("- **NEEDS:** Script font strokes too thin for vinyl — needs bold font or manual stroke thickening")
            report.append("")
    
    report.append("## What Changed")
    report.append("")
    report.append("### Universal Fixes Applied to All Text-Based Designs:")
    report.append("1. **Text → Paths:** All text elements converted to outlined vector paths via Inkscape (required for vinyl cutting)")
    report.append("2. **Subtitle enlarged:** \"MARINE CORPS LEADERSHIP TRAIT\" increased from 20pt to 28pt for legibility at 4-6\" size")
    report.append("3. **Thin strokes thickened:** All strokes under 3pt increased to 3pt minimum for reliable cutting")
    report.append("4. **Low-opacity elements removed:** Elements below 50% opacity stripped (don't translate to vinyl)")
    report.append("")
    report.append("### Design-Specific Fixes:")
    report.append("- **enthusiasm.svg:** Removed sunburst rays, small stars, exclamation marks, wavy divider → clean minimal design")
    report.append("- **semperfi-distressed.svg:** Stripped all distress effects → clean SEMPER FI with straight borders")
    report.append("- **loyalty.svg:** Converted white fills to transparent (single-color compatible)")
    report.append("- **di-cover designs:** Converted gray fills to black (single-color compatible)")
    report.append("")
    report.append("## Next Steps")
    report.append("")
    report.append("1. **Review fixed designs** in `drafts/svg-batch-1-fixed/`")
    report.append("2. **Order test cuts** of the 3 quick-win designs: initiative, marine-veteran, semperfi-minimal")
    report.append("3. **Commission silhouette redesigns** for the 4 figure designs")
    report.append("4. **Decide on jjdidtiebuckle:** Keep as poster format or simplify for vinyl?")
    report.append("5. **Consider adding EGA** to trait designs for stronger Marine identity")
    
    return "\n".join(report)

def main():
    ensure_dirs()
    
    svg_files = get_svg_files()
    print(f"Found {len(svg_files)} SVG files to process")
    
    for filename in svg_files:
        try:
            process_file(filename)
        except Exception as e:
            print(f"  ❌ Error processing {filename}: {e}")
            fixes_log.setdefault(filename, []).append(f"ERROR: {e}")
    
    # Also copy the approved directory if it exists
    approved_src = os.path.join(SRC_DIR, 'approved')
    approved_dst = os.path.join(DST_DIR, 'approved')
    if os.path.isdir(approved_src):
        if os.path.exists(approved_dst):
            shutil.rmtree(approved_dst)
        shutil.copytree(approved_src, approved_dst)
        print(f"\nCopied 'approved' directory")
    
    # Generate report
    report = generate_report()
    report_path = os.path.join(DST_DIR, "FIX-REPORT.md")
    write_svg(report_path, report)
    print(f"\n{'='*60}")
    print(f"Fix report saved to: {report_path}")
    print(f"{'='*60}")
    
    return report

if __name__ == '__main__':
    main()

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