"""
PDF导出服务
使用Playwright/Pyppeteer实现高质量的中文PDF导出
"""

import asyncio
import logging
import sys
from datetime import datetime
from typing import Dict, List, Optional
from pathlib import Path
import tempfile
import base64
import zipfile
import io

from fastapi import HTTPException
from pydantic import BaseModel

# 延迟导入以避免启动时的依赖问题
WEASYPRINT_AVAILABLE = False
PLAYWRIGHT_AVAILABLE = False

def _check_weasyprint():
    global WEASYPRINT_AVAILABLE
    try:
        from weasyprint import HTML
        WEASYPRINT_AVAILABLE = True
        return True
    except (ImportError, OSError):
        WEASYPRINT_AVAILABLE = False
        return False

def _check_playwright():
    global PLAYWRIGHT_AVAILABLE
    try:
        from playwright.async_api import async_playwright
        PLAYWRIGHT_AVAILABLE = True
        return True
    except ImportError:
        PLAYWRIGHT_AVAILABLE = False
        return False

# 检查 Playwright
_check_playwright()

from app.utils.fonts import find_available_font

logger = logging.getLogger(__name__)

class PDFExportRequest(BaseModel):
    """PDF导出请求模型"""
    html_content: str
    filename: Optional[str] = None
    options: Optional[Dict] = {}

class ProcurementPDFData(BaseModel):
    """采购清单PDF数据模型"""
    title: str
    method: str
    export_time: str
    brands: List[str]
    product_count: int
    sku_count: int
    products: List[Dict]

class PDFService:
    """PDF导出服务"""
    
    def __init__(self):
        self.font_path = None
        self._init_fonts()
    
    def _init_fonts(self):
        """初始化字体路径 - 使用跨平台字体工具"""
        self.font_path = find_available_font()
        if self.font_path:
            logger.info(f"Found Chinese font: {self.font_path}")
        else:
            logger.warning("No Chinese font found, will use default fonts")
    
    async def generate_pdf_from_html(self, request: PDFExportRequest, max_retries: int = 3) -> bytes:
        """从HTML生成PDF - 优先使用WeasyPrint，失败时回退到Playwright"""

        # 首先尝试 WeasyPrint (更可靠，如果系统有依赖)
        if _check_weasyprint():
            try:
                pdf_bytes = await self._generate_pdf_with_weasyprint(request)
                logger.info(f"PDF generated with WeasyPrint, size: {len(pdf_bytes)} bytes")
                return pdf_bytes
            except Exception as e:
                logger.warning(f"WeasyPrint failed: {str(e)}, trying Playwright...")

        # 回退到 Playwright
        if PLAYWRIGHT_AVAILABLE:
            return await self._generate_pdf_with_playwright(request, max_retries)

        raise HTTPException(status_code=500, detail="没有可用的PDF生成引擎")

    async def _generate_pdf_with_weasyprint(self, request: PDFExportRequest) -> bytes:
        """使用 WeasyPrint 生成 PDF"""
        import asyncio
        from weasyprint import HTML

        css_content = self._get_chinese_font_css()

        def _generate():
            full_html = request.html_content
            # 如果HTML中没有style标签，添加CSS
            if '<style>' not in full_html:
                full_html = full_html.replace('</head>', f'<style>{css_content}</style></head>')

            html = HTML(string=full_html, base_url='http://localhost:8000')
            pdf_bytes = html.write_pdf()
            return pdf_bytes

        # 在线程池中运行以避免阻塞
        loop = asyncio.get_event_loop()
        pdf_bytes = await loop.run_in_executor(None, _generate)
        return pdf_bytes

    async def _generate_pdf_with_playwright(self, request: PDFExportRequest, max_retries: int = 3) -> bytes:
        """使用 Playwright 生成 PDF - 使用 subprocess 执行独立脚本"""
        import asyncio
        import subprocess
        import json

        html_content = request.html_content
        css_content = self._get_chinese_font_css()
        options = request.options or {}

        # 将CSS注入到HTML中
        if '<style>' not in html_content:
            html_content = html_content.replace('</head>', f'<style>{css_content}</style></head>')

        last_error = None
        for attempt in range(max_retries):
            try:
                # 创建临时HTML文件
                with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False, encoding='utf-8') as f:
                    f.write(html_content)
                    html_path = f.name

                # 创建临时PDF输出路径
                pdf_path = html_path.replace('.html', '.pdf')

                # 使用 subprocess 运行 playwright 脚本
                script = f'''
import sys
from playwright.sync_api import sync_playwright

html_path = "{html_path}"
pdf_path = "{pdf_path}"

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True, args=["--no-sandbox", "--disable-dev-shm-usage"])
    page = browser.new_page()
    page.goto(f"file://{{html_path}}", wait_until="networkidle")
    page.wait_for_timeout(1000)
    page.pdf(path=pdf_path, format="A4", print_background=True,
             margin={{"top": "15mm", "right": "15mm", "bottom": "15mm", "left": "15mm"}})
    browser.close()
print("OK")
'''
                # 在子进程中执行
                proc = await asyncio.create_subprocess_exec(
                    sys.executable, '-c', script,
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.PIPE
                )
                # 服务器资源有限，移除超时限制（设为30分钟）
                stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=1800)

                if proc.returncode != 0:
                    raise Exception(f"Subprocess failed: {stderr.decode()}")

                # 读取生成的PDF
                with open(pdf_path, 'rb') as f:
                    pdf_bytes = f.read()

                # 清理临时文件
                import os
                os.unlink(html_path)
                os.unlink(pdf_path)

                logger.info(f"PDF generated with Playwright subprocess, size: {len(pdf_bytes)} bytes")
                return pdf_bytes

            except Exception as e:
                last_error = e
                logger.warning(f"Playwright attempt {attempt + 1}/{max_retries} failed: {str(e)}")
                # 清理可能存在的临时文件
                try:
                    import os
                    if 'html_path' in locals():
                        os.unlink(html_path)
                    if 'pdf_path' in locals() and os.path.exists(pdf_path):
                        os.unlink(pdf_path)
                except:
                    pass
                if attempt < max_retries - 1:
                    await asyncio.sleep(1)

        logger.error(f"Playwright failed after {max_retries} attempts: {str(last_error)}")
        raise HTTPException(status_code=500, detail=f"PDF生成失败: {str(last_error)}")
    
    def _get_chinese_font_css(self) -> str:
        """获取中文字体CSS"""
        base_css = """
        @page {
            size: A4;
            margin: 15mm;
        }
        
        body {
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Noto Sans SC', 'SimHei', sans-serif;
            font-size: 12px;
            line-height: 1.4;
            color: #333;
            margin: 0;
            padding: 0;
        }
        
        h1, h2, h3, h4, h5, h6 {
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Noto Sans SC', 'SimHei', sans-serif;
            color: #1f2937;
        }
        
        .pdf-title {
            font-size: 20px;
            font-weight: bold;
            text-align: center;
            margin-bottom: 20px;
            color: #1f2937;
        }
        
        .pdf-info {
            font-size: 11px;
            margin-bottom: 15px;
            line-height: 1.6;
        }
        
        .pdf-info-item {
            margin-bottom: 5px;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 10px;
            font-size: 10px;
            table-layout: fixed;
        }
        
        th, td {
            border: 1px solid #e5e7eb;
            padding: 8px 6px;
            text-align: left;
            vertical-align: middle;
            word-wrap: break-word;
        }
        
        th:nth-child(1) { width: 4%; }  /* 序号 */
        th:nth-child(2) { width: 32%; } /* 图片 - 调整到32% */
        th:nth-child(3) { width: 13%; } /* 货号 */
        th:nth-child(4) { width: 9%; } /* 颜色 */
        th:nth-child(5) { width: 21%; } /* 销售属性 */
        th:nth-child(6) { width: 9%; } /* 尺寸 */
        th:nth-child(7) { width: 12%; } /* 数量 */
        
        th {
            background-color: #f3f4f6;
            font-weight: bold;
            color: #374151;
        }
        
        .product-header {
            background-color: #1e40af !important;
            color: white !important;
            font-weight: bold;
        }
        
        .product-header td {
            word-wrap: break-word;
            white-space: normal;
            padding: 10px 8px;
            line-height: 1.4;
        }
        
        .sku-row {
            background-color: #fafafa;
        }
        
        .sku-row-same-color {
            border-top: none !important;
        }
        
        .sku-row-same-color td:first-child {
            border-top: none;
        }
        
        .image-cell {
            text-align: center;
            vertical-align: middle;
            padding: 5px;
            padding-left: 10px;
            min-width: 220px;
            height: 185px;
        }
        
        .image-cell-empty {
            border-right: 1px solid #e5e7eb;
        }
        
        .merged-cell {
            vertical-align: middle !important;
            text-align: center;
        }
        
        .quantity-cell {
            font-size: 14px;
            font-weight: bold;
            color: #dc2626;
            text-align: center;
        }
        
        .image-cell img {
            width: 195px;
            height: 195px;
            border-radius: 4px;
            border: 1px solid #e5e7eb;
            object-fit: cover;
            display: block;
            margin: 0 auto 0 calc(50% - 97.5px - 2px);
        }
        
        .no-image {
            width: 195px;
            height: 195px;
            background-color: #f3f4f6;
            border: 1px dashed #d1d5db;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            color: #6b7280;
            font-size: 13px;
            border-radius: 4px;
            margin: 0 auto 0 calc(50% - 97.5px - 2px);
        }
        
        .page-footer {
            position: fixed;
            bottom: 0;
            width: 100%;
            font-size: 8px;
            color: #6b7280;
            border-top: 1px solid #e5e7eb;
            padding-top: 5px;
        }
        
        .text-right {
            text-align: right;
        }
        
        .no-break {
            page-break-inside: avoid;
        }
        """
        
        # 如果找到了中文字体文件，添加@font-face声明
        if self.font_path:
            font_css = f"""
            @font-face {{
                font-family: 'CustomChinese';
                src: url('file://{self.font_path}') format('truetype');
                font-weight: normal;
                font-style: normal;
            }}
            
            body, h1, h2, h3, h4, h5, h6, th, td {{
                font-family: 'CustomChinese', 'PingFang SC', 'Microsoft YaHei', sans-serif;
            }}
            """
            base_css = font_css + base_css
        
        return base_css
    
    def _generate_image_html(self, image_url: str, product_name: str = "") -> str:
        """
        生成图片HTML标签，统一处理各种格式的图片URL
        
        Args:
            image_url: 图片URL或路径
            product_name: 产品名称，用于调试日志
        
        Returns:
            图片HTML标签或无图片提示
        """
        if not image_url:
            return '<div class="no-image">无图片</div>'
        
        # 统一处理图片URL
        full_image_url = None
        
        # 处理各种路径格式
        if image_url.startswith('http://') or image_url.startswith('https://'):
            # 完整的HTTP/HTTPS URL，直接使用
            full_image_url = image_url
        elif image_url.startswith('/api/v1/static/'):
            # 已经有API前缀，添加域名
            full_image_url = f'http://localhost:8000{image_url}'
        elif image_url.startswith('images/'):
            # 相对路径，添加完整API路径
            full_image_url = f'http://localhost:8000/api/v1/static/{image_url}'
        elif image_url.startswith('/images/'):
            # 绝对路径（以/开头），去掉开头的/并添加API路径
            full_image_url = f'http://localhost:8000/api/v1/static{image_url}'
        else:
            # 其他格式，假设是文件名，添加到images目录
            full_image_url = f'http://localhost:8000/api/v1/static/images/{image_url}'
        
        logger.debug(f"Processing image for {product_name}: original={image_url}, full={full_image_url}")
        
        # 生成图片HTML标签，添加错误处理
        return f'''<img src="{full_image_url}" 
                       style="width: 195px; height: 195px; border-radius: 4px; object-fit: cover;"
                       onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"
                       alt="{product_name}"/>
                   <div class="no-image" style="display:none;">无图片</div>'''
    
    def generate_procurement_html(self, data: ProcurementPDFData) -> str:
        """生成采购清单HTML"""
        # 构建品牌列表文本
        brand_text = ", ".join(data.brands[:5])
        if len(data.brands) > 5:
            brand_text += f" 等{len(data.brands)}个品牌"
        
        # 开始构建HTML
        html_parts = [
            '<!DOCTYPE html>',
            '<html lang="zh-CN">',
            '<head>',
            '<meta charset="UTF-8">',
            '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
            '<title>采购清单</title>',
            '</head>',
            '<body>',
            
            # 标题
            f'<div class="pdf-title">{data.title}</div>',
            
            # 基本信息
            '<div class="pdf-info">',
            f'<div class="pdf-info-item">导出时间: {data.export_time}</div>',
            f'<div class="pdf-info-item">采购方式: {data.method}</div>',
            f'<div class="pdf-info-item">涉及品牌: {brand_text}</div>',
            f'<div class="pdf-info-item">商品总数: {data.product_count} 个商品</div>',
            f'<div class="pdf-info-item">SKU总数: {data.sku_count} 个</div>',
            '</div>',
            
            # 表格
            '<table>',
            '<thead>',
            '<tr>',
            '<th>序号</th>',
            '<th>图片</th>',
            '<th>货号</th>',
            '<th>颜色</th>',
            '<th>销售属性</th>',
            '<th>尺寸</th>',
            '<th>数量</th>',
            '</tr>',
            '</thead>',
            '<tbody>'
        ]
        
        # 添加产品数据
        total_quantity_all = 0  # 累积所有商品的总数量
        row_number = 0  # 全局行号
        
        for product_index, product in enumerate(data.products, 1):
            product_name = product.get('product_name', '')
            skus = product.get('skus', [])
            
            # 显示完整的产品名称，不截断
            html_parts.append(
                f'<tr class="product-header no-break">'
                f'<td colspan="7" style="word-wrap: break-word; white-space: normal;">{product_index}. {product_name}</td>'
                f'</tr>'
            )
            
            # 按颜色组织SKU，实现同颜色聚合
            color_sku_map = {}
            for sku in skus:
                color = sku.get('color', '默认')
                if color not in color_sku_map:
                    color_sku_map[color] = []
                color_sku_map[color].append(sku)
            
            # 为每个颜色组生成合并的表格行
            for color, color_skus in color_sku_map.items():
                rowspan = len(color_skus)  # 计算需要合并的行数
                
                for sku_index, sku in enumerate(color_skus):
                    row_number += 1
                    
                    # 获取SKU信息
                    product_code = sku.get('product_code', '-')
                    size = sku.get('size', '-')
                    quantity = sku.get('quantity', 0)
                    sales_attr = sku.get('sales_attr', '-')
                    
                    # Debug logging
                    logger.info(f"Processing SKU in PDF: product_code={product_code}, quantity={quantity}, image_url={sku.get('image_url', 'NO_IMAGE')}")
                    
                    # 累加总数量
                    total_quantity_all += quantity
                    
                    # 第一行：显示合并的单元格（序号、图片、货号、颜色）
                    if sku_index == 0:
                        # 处理图片URL - 统一化处理逻辑
                        image_url = sku.get('image_url', '')
                        image_html = self._generate_image_html(image_url, product_name)
                        logger.debug(f"Generated image HTML for {product_name}: {image_html[:100]}...")
                        
                        # 生成带rowspan的第一行
                        html_parts.append(
                            f'<tr class="sku-row">'
                            f'<td rowspan="{rowspan}" class="merged-cell">{row_number - sku_index}</td>'
                            f'<td rowspan="{rowspan}" class="image-cell merged-cell">{image_html}</td>'
                            f'<td rowspan="{rowspan}" class="merged-cell">{product_code}</td>'
                            f'<td rowspan="{rowspan}" class="merged-cell">{color}</td>'
                            f'<td style="word-wrap: break-word; max-width: 200px;">{sales_attr}</td>'
                            f'<td style="text-align: center;">{size}</td>'
                            f'<td class="quantity-cell">{quantity}</td>'
                            f'</tr>'
                        )
                    else:
                        # 后续行：只显示销售属性、尺寸、数量（前面的单元格已被合并）
                        html_parts.append(
                            f'<tr class="sku-row">'
                            f'<td style="word-wrap: break-word; max-width: 200px;">{sales_attr}</td>'
                            f'<td style="text-align: center;">{size}</td>'
                            f'<td class="quantity-cell">{quantity}</td>'
                            f'</tr>'
                        )
        
        # 结束HTML
        html_parts.extend([
            '</tbody>',
            '</table>',
            
            # Total 总计（红色显示）
            '<div style="margin-top: 20px; text-align: right; font-size: 16px; font-weight: bold; color: #dc2626;">',
            f'Total: {total_quantity_all} 个商品',
            '</div>',
            
            # 页脚
            '<div class="page-footer">',
            f'<div style="float: left;">生成时间: {data.export_time}</div>',
            f'<div style="float: right;">总计: {data.sku_count} 个SKU</div>',
            '<div style="clear: both;"></div>',
            '</div>',
            
            '</body>',
            '</html>'
        ])
        
        return '\n'.join(html_parts)
    
    async def generate_procurement_images(self, selected_skus: List[Dict]) -> List[Dict]:
        """批量生成采购SKU图片，返回图片数据列表"""
        try:
            images = []
            
            async with async_playwright() as p:
                # 启动浏览器时使用更好的参数以提高图片质量
                browser = await p.chromium.launch(
                    headless=True,
                    args=[
                        '--no-sandbox', 
                        '--disable-dev-shm-usage',
                        '--disable-web-security',
                        '--disable-features=VizDisplayCompositor',
                        '--force-color-profile=srgb',
                        '--disable-lcd-text'
                    ]
                )
                
                for index, sku in enumerate(selected_skus):
                    # 生成单个SKU的图片
                    image_bytes = await self._generate_single_sku_image(browser, sku, index)
                    
                    # 生成文件名
                    product_code = sku.get('product_code', f'SKU_{index+1}')
                    color = sku.get('color', '').replace('/', '_')  # 避免路径问题
                    filename = f"{product_code}_{color}_{index+1}.jpg"
                    
                    images.append({
                        'filename': filename,
                        'data': image_bytes
                    })
                    logger.info(f"Generated image for SKU {index+1}: {filename}")
                
                await browser.close()
            
            return images
            
        except Exception as e:
            logger.error(f"Procurement images generation failed: {str(e)}")
            raise HTTPException(status_code=500, detail=f"图片生成失败: {str(e)}")
    
    async def _generate_single_sku_image(self, browser, sku: Dict, index: int) -> bytes:
        """生成单个SKU的图片"""
        # 创建高DPI页面以提高图片质量
        context = await browser.new_context(
            viewport={'width': 900, 'height': 1080},  # 3倍分辨率
            device_scale_factor=3  # 设备像素比为3，提高清晰度
        )
        page = await context.new_page()
        
        # 构建HTML内容
        html_content = self._generate_sku_image_html(sku)
        await page.set_content(html_content, wait_until='domcontentloaded')
        
        # 等待字体加载
        await page.evaluate("() => document.fonts.ready")
        
        # 等待图片加载完成
        await page.evaluate("""
            () => {
                return new Promise((resolve) => {
                    const images = document.querySelectorAll('img');
                    if (images.length === 0) {
                        resolve();
                        return;
                    }
                    
                    let loadedCount = 0;
                    const totalImages = images.length;
                    
                    const checkComplete = () => {
                        loadedCount++;
                        if (loadedCount === totalImages) {
                            resolve();
                        }
                    };
                    
                    images.forEach(img => {
                        if (img.complete && img.naturalHeight !== 0) {
                            checkComplete();
                        } else {
                            img.addEventListener('load', checkComplete);
                            img.addEventListener('error', checkComplete);
                        }
                    });
                });
            }
        """)
        
        # 额外等待确保渲染完成
        await page.wait_for_timeout(500)
        await page.wait_for_load_state('networkidle')
        
        # 截图为JPG格式，使用高质量设置
        screenshot_bytes = await page.screenshot(
            type='jpeg',
            quality=95,  # 提高JPG质量
            clip={'x': 0, 'y': 0, 'width': 900, 'height': 1080}  # 3倍尺寸截图
        )
        
        await context.close()
        return screenshot_bytes
    
    def _generate_sku_image_html(self, sku: Dict) -> str:
        """生成SKU图片的HTML内容"""
        # 处理图片URL
        image_urls = sku.get('image_urls', [])
        if image_urls and len(image_urls) > 0:
            image_url = image_urls[0]
            if image_url.startswith('/api/v1/static/images/'):
                image_url = f'http://localhost:8000{image_url}'
        else:
            image_url = ""
        
        # 处理尺寸和数量信息以及对应的销售属性
        size_quantity_text = ""
        sales_attr_text = ""
        
        if sku.get('size_details') and len(sku['size_details']) > 0:
            # 有size_details，处理每个尺寸及其对应的销售属性
            size_quantity_pairs = []
            sales_attr_list = []
            base_sales_attr = sku.get('sales_attr', '')
            
            for size_detail in sku['size_details']:
                size_value = size_detail.get('size', '')
                quantity = size_detail.get('quantity', 0)
                if size_value:
                    size_quantity_pairs.append(f"{size_value}({quantity})")
                    
                    # 为每个尺寸生成对应的销售属性
                    if base_sales_attr:
                        if size_value not in base_sales_attr:
                            size_specific_attr = f"{base_sales_attr}_{size_value}"
                        else:
                            size_specific_attr = base_sales_attr
                        sales_attr_list.append(size_specific_attr)
            
            size_quantity_text = ' '.join(size_quantity_pairs)
            sales_attr_text = ' '.join(sales_attr_list)  # 多个销售属性用空格连接
        else:
            # 没有size_details，使用传统的size和销售属性
            size_value = sku.get('size', '')
            quantity = sku.get('pending_procurement_qty', 0)
            base_sales_attr = sku.get('sales_attr', '')
            
            if size_value:
                size_quantity_text = f"{size_value}({quantity})"
                # 为单个尺寸生成销售属性
                if base_sales_attr:
                    if size_value not in base_sales_attr:
                        sales_attr_text = f"{base_sales_attr}_{size_value}"
                    else:
                        sales_attr_text = base_sales_attr
            else:
                sales_attr_text = base_sales_attr
        
        # 基本信息
        brand = sku.get('brand', '')
        product_code = sku.get('product_code', '')
        color = sku.get('color', '')
        product_name = sku.get('product_name', '')
        
        # 构建HTML
        html = f"""
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>SKU图片</title>
            <style>
                body {{
                    margin: 0;
                    padding: 0;
                    width: 900px;
                    font-family: 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', sans-serif;
                    background: white;
                }}
                .container {{
                    width: 900px;
                    background: white;
                }}
                .image-section {{
                    width: 900px;
                    height: 900px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    background: #f8f9fa;
                    border: 3px solid #dee2e6;
                }}
                .image-section img {{
                    max-width: 870px;
                    max-height: 870px;
                    object-fit: contain;
                }}
                .no-image {{
                    color: #6c757d;
                    font-size: 42px;
                }}
                .info-section {{
                    width: 900px;
                    height: 180px;
                    background: white;
                    padding: 24px;
                    box-sizing: border-box;
                    border-left: 3px solid #dee2e6;
                    border-right: 3px solid #dee2e6;
                    border-bottom: 3px solid #dee2e6;
                    display: flex;
                    flex-direction: column;
                    justify-content: center;
                }}
                .info-line1 {{
                    font-size: 36px;
                    font-weight: bold;
                    color: #dc2626;
                    margin-bottom: 12px;
                    line-height: 1.3;
                    word-wrap: break-word;
                    overflow-wrap: break-word;
                }}
                .info-line2 {{
                    font-size: 18px;
                    color: #dc2626;
                    line-height: 1.3;
                    word-wrap: break-word;
                    overflow-wrap: break-word;
                    display: -webkit-box;
                    -webkit-line-clamp: 2;
                    -webkit-box-orient: vertical;
                    overflow: hidden;
                }}
            </style>
        </head>
        <body>
            <div class="container">
                <div class="image-section">
                    {f'<img src="{image_url}" alt="商品图片">' if image_url else '<div class="no-image">无图片</div>'}
                </div>
                <div class="info-section">
                    <div class="info-line1">{brand} {product_code} {color} {size_quantity_text}</div>
                    <div class="info-line2">{sales_attr_text}</div>
                </div>
            </div>
        </body>
        </html>
        """
        
        return html

# 全局PDF服务实例
pdf_service = PDFService()