#!/usr/bin/env python3
"""
Test script for Debian bug #1132566
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1132566

Bug: openpyxl crashes when saving a workbook multiple times with images
     because file handles are closed and reused.

How to run:
    python3 test_bug_1132566.py

Expected result (with patch applied):
    ALL tests PASS — no ValueError or OSError about closed files.

Expected result (WITHOUT patch):
    FAIL on save #2 or later with something like:
    ValueError: I/O operation on closed file
    or: OSError: seek of closed file
"""

import sys
import os
import tempfile
from io import BytesIO

try:
    from openpyxl import Workbook
    from openpyxl.drawing.image import Image as XLImage
    from PIL import Image as PILImage
except ImportError as e:
    print(f"[ERRO] Dependência não encontrada: {e}")
    print("       Instale com: pip install openpyxl pillow")
    sys.exit(1)

PASS = "\033[92mPASS\033[0m"
FAIL = "\033[91mFAIL\033[0m"

results = []

def make_test_image_bytes(color=(255, 0, 0), size=(50, 50), fmt="PNG"):
    """Cria uma imagem PIL simples em memória e retorna como BytesIO."""
    buf = BytesIO()
    img = PILImage.new("RGB", size, color=color)
    img.save(buf, format=fmt)
    buf.seek(0)
    return buf


def run_test(name, fn):
    try:
        fn()
        print(f"  [{PASS}] {name}")
        results.append(True)
    except Exception as e:
        print(f"  [{FAIL}] {name}")
        print(f"          {type(e).__name__}: {e}")
        results.append(False)


# ──────────────────────────────────────────────
# SCENARIO 1: Image from BytesIO
# Saves the same workbook 3 times consecutively
# ──────────────────────────────────────────────
def test_bytesio_multiple_saves():
    buf = make_test_image_bytes()
    img = XLImage(buf)

    wb = Workbook()
    ws = wb.active
    ws.add_image(img, "B2")

    with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
        path = f.name

    try:
        wb.save(path)   # save 1
        wb.save(path)   # save 2  ← costumava travar aqui
        wb.save(path)   # save 3
    finally:
        os.unlink(path)


# ──────────────────────────────────────────────
# SCENARIO 2: image from file on disk
# ──────────────────────────────────────────────
def test_file_path_multiple_saves():
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
        img_path = tf.name
        pil = PILImage.new("RGB", (50, 50), color=(0, 200, 0))
        pil.save(tf, format="PNG")

    try:
        img = XLImage(img_path)
        wb = Workbook()
        ws = wb.active
        ws.add_image(img, "C3")

        with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
            xlsx_path = f.name

        try:
            wb.save(xlsx_path)
            wb.save(xlsx_path)
            wb.save(xlsx_path)
        finally:
            os.unlink(xlsx_path)
    finally:
        os.unlink(img_path)


# ──────────────────────────────────────────────
# SCENARIO 3: Multiple images in the same workbook
# ──────────────────────────────────────────────
def test_multiple_images_multiple_saves():
    wb = Workbook()
    ws = wb.active

    for i, color in enumerate([(255, 0, 0), (0, 255, 0), (0, 0, 255)]):
        buf = make_test_image_bytes(color=color)
        img = XLImage(buf)
        ws.add_image(img, f"A{i * 15 + 1}")

    with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
        path = f.name

    try:
        wb.save(path)
        wb.save(path)
        wb.save(path)
    finally:
        os.unlink(path)


# ──────────────────────────────────────────────
# 
SCENARIO 4: BytesIO explicitly closed before the second save
# This is the exact case reported in the bug
# ──────────────────────────────────────────────
def test_explicitly_closed_bytesio():
    buf = make_test_image_bytes(color=(128, 128, 0))
    img = XLImage(buf)

    wb = Workbook()
    ws = wb.active
    ws.add_image(img, "D4")

    with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
        path = f.name

    try:
        wb.save(path)       # primeiro save — fecha o fp internamente
        buf.close()         # simula o handle fechado
        wb.save(path)       # segundo save — deve sobreviver com o patch
    finally:
        os.unlink(path)


# ──────────────────────────────────────────────
# SCENARIO 5: JPEG (does not convert to PNG)
# ──────────────────────────────────────────────
def test_jpeg_multiple_saves():
    buf = make_test_image_bytes(color=(200, 100, 50), fmt="JPEG")
    img = XLImage(buf)

    wb = Workbook()
    ws = wb.active
    ws.add_image(img, "E5")

    with tempfile.NamedTemporaryFile(suffix=".xlsx", delete=False) as f:
        path = f.name

    try:
        wb.save(path)
        wb.save(path)
    finally:
        os.unlink(path)


# ──────────────────────────────────────────────
# Execution
# ──────────────────────────────────────────────
print()
print("=" * 55)
print("  Teste: bug Debian #1132566 — openpyxl image crash")
print("=" * 55)
print()

run_test("BytesIO salvo 3x consecutivas",           test_bytesio_multiple_saves)
run_test("Arquivo em disco salvo 3x consecutivas",  test_file_path_multiple_saves)
run_test("Múltiplas imagens salvas 3x",             test_multiple_images_multiple_saves)
run_test("BytesIO fechado explicitamente (caso exato do bug)", test_explicitly_closed_bytesio)
run_test("JPEG salvo 2x consecutivas",              test_jpeg_multiple_saves)

print()
total = len(results)
passed = sum(results)
failed = total - passed

print(f"  Resultado: {passed}/{total} testes passaram", end="")
if failed == 0:
    print(f"  \033[92m✓ patch OK — bug corrigido\033[0m")
else:
    print(f"  \033[91m✗ {failed} teste(s) falharam — verificar patch\033[0m")
print()

sys.exit(0 if failed == 0 else 1)

