from pathlib import Path
import hashlib
from urllib.request import Request, urlopen

from PIL import Image
from reportlab.lib.colors import Color, HexColor, white
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas


ROOT = Path(__file__).resolve().parent
ASSETS = ROOT / "assets"
OUTPUT = ROOT / "output" / "智慧食堂客户方案说明_V1.0_20260828.pdf"
FONT_PATH = Path("/System/Library/Fonts/Supplemental/Arial Unicode.ttf")

ORANGE = HexColor("#F36C21")
NAVY = HexColor("#17324D")
PURPLE = HexColor("#6F5A9E")
TEAL = HexColor("#2B8C84")
TEXT = HexColor("#243342")
MUTED = HexColor("#657384")
LIGHT = HexColor("#F4F6F8")
LINE = HexColor("#DCE2E8")
ASSET_SOURCES = {
    "smart-canteen-concept-v1.png": (
        "https://img.kxunpt.cn/public/ai-marketing-platform/work/2026-08-28-odoo-customer-portal-launch/smart-canteen-concept-v1.png",
        "f6aefc244a6e58e3687deb981e6a49a4394b0c95fcb7bab70bc9541272f8ea6e",
    ),
    "cpt-logo-orange.png": (
        "https://img.kxunpt.cn/public/ai-marketing-platform/work/2026-08-28-odoo-customer-portal-launch/cpt-logo-orange.png",
        "58c87931a17ddf738b8ad020c7cba11ea0aa2a4bde6ed5a8501cd02ade89f912",
    ),
}


def ensure_assets():
    ASSETS.mkdir(parents=True, exist_ok=True)
    for filename, (url, expected_sha256) in ASSET_SOURCES.items():
        target = ASSETS / filename
        if target.exists() and hashlib.sha256(target.read_bytes()).hexdigest() == expected_sha256:
            continue
        request = Request(url, headers={"User-Agent": "CPT-PDF-Generator/1.0"})
        with urlopen(request, timeout=30) as response:
            payload = response.read()
        if hashlib.sha256(payload).hexdigest() != expected_sha256:
            raise RuntimeError(f"资产校验失败：{filename}")
        target.write_bytes(payload)


def register_fonts():
    pdfmetrics.registerFont(TTFont("CPTSans", str(FONT_PATH)))


def draw_image_cover(c, path, x, y, w, h):
    with Image.open(path) as image:
        iw, ih = image.size
    scale = max(w / iw, h / ih)
    sw, sh = iw * scale, ih * scale
    c.saveState()
    clip = c.beginPath()
    clip.rect(x, y, w, h)
    c.clipPath(clip, stroke=0, fill=0)
    c.drawImage(str(path), x + (w - sw) / 2, y + (h - sh) / 2, sw, sh, mask="auto")
    c.restoreState()


def draw_header(c, title, page_no):
    width, height = A4
    c.drawImage(str(ASSETS / "cpt-logo-orange.png"), 36, height - 48, width=55, height=18, preserveAspectRatio=True, mask="auto")
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 9)
    c.drawRightString(width - 36, height - 39, "康比特数字体育科技")
    c.setStrokeColor(LINE)
    c.line(36, height - 56, width - 36, height - 56)
    c.setFillColor(MUTED)
    c.setFont("CPTSans", 8)
    c.drawString(36, 25, title)
    c.drawRightString(width - 36, 25, f"{page_no} / 5")


def wrap(c, text, x, y, max_width, font_size=10, leading=16, color=TEXT):
    c.setFont("CPTSans", font_size)
    c.setFillColor(color)
    line = ""
    cursor = y
    for char in text:
        candidate = line + char
        if pdfmetrics.stringWidth(candidate, "CPTSans", font_size) > max_width and line:
            c.drawString(x, cursor, line)
            cursor -= leading
            line = char
        else:
            line = candidate
    if line:
        c.drawString(x, cursor, line)
        cursor -= leading
    return cursor


def section_title(c, number, title, y):
    c.setFillColor(ORANGE)
    c.roundRect(36, y - 4, 26, 26, 6, fill=1, stroke=0)
    c.setFillColor(white)
    c.setFont("CPTSans", 11)
    c.drawCentredString(49, y + 4, number)
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 20)
    c.drawString(74, y, title)


def bullet(c, text, x, y, max_width, accent=ORANGE):
    c.setFillColor(accent)
    c.circle(x + 4, y + 4, 2.5, fill=1, stroke=0)
    return wrap(c, text, x + 16, y, max_width - 16, 10, 16)


def card(c, x, y, w, h, title, subtitle, accent, items):
    c.setFillColor(white)
    c.setStrokeColor(LINE)
    c.roundRect(x, y, w, h, 12, fill=1, stroke=1)
    c.setFillColor(accent)
    c.roundRect(x, y + h - 54, w, 54, 12, fill=1, stroke=0)
    c.rect(x, y + h - 54, w, 12, fill=1, stroke=0)
    c.setFillColor(white)
    c.setFont("CPTSans", 15)
    c.drawString(x + 18, y + h - 31, title)
    c.setFont("CPTSans", 8)
    c.drawString(x + 18, y + h - 45, subtitle)
    cursor = y + h - 78
    for item in items:
        cursor = bullet(c, item, x + 16, cursor, w - 32, accent)


def build_pdf():
    ensure_assets()
    register_fonts()
    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    c = canvas.Canvas(str(OUTPUT), pagesize=A4, pageCompression=1)
    c.setTitle("智慧食堂客户方案说明 V1.0")
    c.setAuthor("康比特数字体育科技")
    width, height = A4

    # Page 1 - cover
    c.setFillColor(NAVY)
    c.rect(0, 0, width, height, fill=1, stroke=0)
    c.setFillColor(ORANGE)
    c.rect(0, height - 12, width, 12, fill=1, stroke=0)
    c.drawImage(str(ASSETS / "cpt-logo-orange.png"), 42, height - 80, width=90, height=30, preserveAspectRatio=True, mask="auto")
    c.setFillColor(white)
    c.setFont("CPTSans", 30)
    c.drawString(42, height - 150, "智慧食堂客户方案说明")
    c.setFillColor(Color(1, 1, 1, alpha=0.78))
    c.setFont("CPTSans", 13)
    c.drawString(42, height - 180, "从需求测算、产品配置到报价确认，同一方案在线协同")
    draw_image_cover(c, ASSETS / "smart-canteen-concept-v1.png", 0, 92, width, 430)
    c.setFillColor(Color(0.05, 0.10, 0.16, alpha=0.82))
    c.rect(0, 92, width, 44, fill=1, stroke=0)
    c.setFillColor(white)
    c.setFont("CPTSans", 9)
    c.drawString(42, 109, "方案效果示意，非现场实景；最终产品数量、型号和价格以正式报价单为准")
    c.setFillColor(Color(1, 1, 1, alpha=0.72))
    c.setFont("CPTSans", 9)
    c.drawString(42, 52, "版本 V1.0  |  2026-08-28  |  康比特数字体育科技")
    c.showPage()

    # Page 2 - customer portal
    draw_header(c, "智慧食堂客户方案说明", 2)
    section_title(c, "01", "一条链接看完整方案", height - 105)
    wrap(c, "销售人员完成配置和商务审核后，系统生成客户专属报价入口。手机、平板和电脑打开同一安全链接，看到的是同一份产品清单、数量、价格和版本。", 42, height - 150, width - 84, 12, 20)
    steps = [
        ("1", "需求与测算", "就餐人数、菜品数、高峰时长、餐线和结算点"),
        ("2", "方案与配置", "标准套餐骨架、必选产品、可选产品和实施服务"),
        ("3", "商务与报价", "销售价格、有效期、客户版说明和正式报价单"),
        ("4", "在线查看", "手机与 PC 查看、下载 PDF、按授权完成在线确认"),
    ]
    y = height - 245
    for index, title, desc in steps:
        c.setFillColor(LIGHT)
        c.setStrokeColor(LINE)
        c.roundRect(42, y, width - 84, 72, 10, fill=1, stroke=1)
        c.setFillColor(PURPLE if index in ("2", "4") else TEAL)
        c.circle(73, y + 36, 18, fill=1, stroke=0)
        c.setFillColor(white)
        c.setFont("CPTSans", 13)
        c.drawCentredString(73, y + 31, index)
        c.setFillColor(NAVY)
        c.setFont("CPTSans", 13)
        c.drawString(106, y + 43, title)
        c.setFillColor(MUTED)
        c.setFont("CPTSans", 9)
        c.drawString(106, y + 22, desc)
        y -= 90
    c.setFillColor(HexColor("#FFF5EE"))
    c.roundRect(42, 86, width - 84, 70, 10, fill=1, stroke=0)
    c.setFillColor(ORANGE)
    c.setFont("CPTSans", 11)
    c.drawString(58, 128, "客户可见边界")
    wrap(c, "只展示产品名称、对外型号、数量、销售价格、方案价值和已批准资料；所有非客户公开信息均不进入本资料。", 58, 106, width - 116, 9, 15)
    c.showPage()

    # Page 3 - package skeletons
    draw_header(c, "智慧食堂客户方案说明", 3)
    section_title(c, "02", "标准方案骨架", height - 105)
    wrap(c, "套餐先形成完整业务骨架，具体设备数量再依据人数、菜品、高峰、现场动线和实施条件测算。报价页面中的数量与金额是本项目最终口径。", 42, height - 150, width - 84, 11, 18)
    card(c, 42, 348, 245, 300, "自助称重餐线", "410 标准大屏绑盘版", TEAL, [
        "管理软件或结算平台",
        "称重结算终端与配套布菲炉",
        "绑盘终端、餐盘及身份方式",
        "网络、实施、培训和跟餐服务",
        "手机端、大屏、价签等按需选配",
    ])
    card(c, 308, 348, 245, 300, "AI 菜品识别餐线", "S20A / S30A / S40A 按场景选型", PURPLE, [
        "管理软件和账户支付配置",
        "AI 视觉识别结算终端",
        "结算通道按高峰吞吐测算",
        "人工收银和异常纠错作为兜底",
        "网络、实施、培训和维保服务",
    ])
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 14)
    c.drawString(42, 302, "测算依据")
    metrics = ["总注册人数", "各餐次人数", "高峰人数与时长", "同时供应菜品数", "餐线占比", "岛台/结算点", "餐盘周转", "部署与网络"]
    x, y = 42, 263
    for i, metric in enumerate(metrics):
        c.setFillColor(LIGHT)
        c.roundRect(x, y, 118, 30, 8, fill=1, stroke=0)
        c.setFillColor(TEXT)
        c.setFont("CPTSans", 9)
        c.drawCentredString(x + 59, y + 10, metric)
        x += 130
        if (i + 1) % 4 == 0:
            x = 42
            y -= 44
    c.setFillColor(HexColor("#EEF7F6"))
    c.roundRect(42, 91, width - 84, 80, 10, fill=1, stroke=0)
    c.setFillColor(TEAL)
    c.setFont("CPTSans", 11)
    c.drawString(58, 140, "数量原则")
    wrap(c, "称重菜位首先由同时供应菜品和有效菜位决定，再用高峰人数校正重复点位；绑盘、AI结算和消费机按高峰吞吐测算；餐盘按就餐人数、周转和备用系数测算。", 58, 117, width - 116, 9, 15)
    c.showPage()

    # Page 4 - concept visual
    draw_header(c, "智慧食堂客户方案说明", 4)
    section_title(c, "03", "方案效果示意", height - 105)
    wrap(c, "示意图表达托盘识别、自助选餐、智能称重、AI识别结算和运营看板的整体动线，不代表任何具体客户现场，也不作为设备数量或验收证据。", 42, height - 150, width - 84, 10, 16)
    draw_image_cover(c, ASSETS / "smart-canteen-concept-v1.png", 42, 195, width - 84, 440)
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 12)
    c.drawString(42, 157, "从进入到结算的主要流程")
    flow = "托盘/身份识别  >  自助选餐  >  实时称重或AI识别  >  账户结算  >  经营分析"
    c.setFillColor(LIGHT)
    c.roundRect(42, 103, width - 84, 36, 8, fill=1, stroke=0)
    c.setFillColor(PURPLE)
    c.setFont("CPTSans", 9)
    c.drawCentredString(width / 2, 116, flow)
    c.showPage()

    # Page 5 - case boundary and next step
    draw_header(c, "智慧食堂客户方案说明", 5)
    section_title(c, "04", "案例、报价与确认边界", height - 105)
    c.setFillColor(LIGHT)
    c.setStrokeColor(LINE)
    c.roundRect(42, 502, width - 84, 166, 12, fill=1, stroke=1)
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 15)
    c.drawString(62, 626, "真实项目案例")
    wrap(c, "真实客户案例、现场照片和项目效果图只有在获得明确外发授权后，才由商务人员加入客户方案。本通用版本不公开未授权客户名称、现场资料或项目过程数据。", 62, 594, width - 124, 11, 19)
    c.setFillColor(ORANGE)
    c.roundRect(62, 526, 150, 32, 8, fill=1, stroke=0)
    c.setFillColor(white)
    c.setFont("CPTSans", 9)
    c.drawCentredString(137, 537, "经授权后按客户场景提供")

    c.setFillColor(white)
    c.setStrokeColor(LINE)
    c.roundRect(42, 278, width - 84, 190, 12, fill=1, stroke=1)
    c.setFillColor(NAVY)
    c.setFont("CPTSans", 15)
    c.drawString(62, 426, "正式报价以在线页面为准")
    y = 392
    for item in [
        "产品名称、型号、数量、单位、销售单价和总价",
        "方案版本、有效期、选配项和实施服务",
        "客户版 PDF 下载和商务确认后的附件",
        "如启用在线签署，由双方授权人员完成确认",
    ]:
        y = bullet(c, item, 62, y, width - 124, TEAL)

    c.setFillColor(NAVY)
    c.roundRect(42, 89, width - 84, 146, 12, fill=1, stroke=0)
    c.setFillColor(white)
    c.setFont("CPTSans", 17)
    c.drawString(62, 194, "下一步")
    wrap(c, "请销售人员根据客户真实人数、菜品、高峰和现场条件完成测算，商务审核后再发送客户门户链接。客户可在手机或 PC 查看同一份报价和资料。", 62, 160, width - 124, 11, 19, white)
    c.setFillColor(ORANGE)
    c.setFont("CPTSans", 10)
    c.drawString(62, 112, "正式入口：https://odoo.cpt-fit.com")
    c.showPage()

    c.save()
    print(OUTPUT)


if __name__ == "__main__":
    build_pdf()
