"""商品データ（CSV）から、ECサイト用の商品説明文をまとめて作り、Excelに書き出すツール。

使い方:
    python make_copy.py products.csv                 # Claude で説明文を作成 → チェック → Excel
    python make_copy.py products.csv --from descriptions.json   # 作成済みの説明文をチェックしてExcelにする

チェック（自動）:
    ・文字数（キャッチ30字以内、本文120〜220字）
    ・説明文に出てくる数字が、商品データ（価格・仕様・サイズ・特徴メモ）にあるか
      → データにない数字は「事実と違う可能性」として要確認にする
    ・誇大な表現（最強・絶対・治る・No.1 など）を使っていないか
"""

import argparse
import csv
import json
import re
import sys

from openpyxl import Workbook
from openpyxl.styles import Alignment, Font, PatternFill
from pydantic import BaseModel

NG_WORDS = ["最強", "絶対", "必ず", "治る", "治ります", "No.1", "日本一", "世界一", "完璧", "100%"]


class Copy(BaseModel):
    catch: str  # キャッチコピー（30字以内）
    body: str  # 商品説明（120〜220字）
    points: list[str]  # おすすめポイント3つ
    keywords: list[str]  # 検索用キーワード5つ


def generate(product: dict) -> Copy:
    import anthropic  # APIを使うときだけ読み込む

    client = anthropic.Anthropic()  # ANTHROPIC_API_KEY を環境変数から読む
    facts = "\n".join(f"{k}：{v}" for k, v in product.items())
    response = client.messages.parse(
        model="claude-opus-5-5",
        max_tokens=4000,
        system=(
            "あなたはECサイトの商品説明を書くコピーライターです。"
            "商品データに書かれた事実だけを使い、書かれていない効果・数字・比較は書かないでください。"
            "誇大な表現（最強・絶対・治るなど）や医療効果をうたう表現は使いません。"
            "やさしい敬体で、使う場面が思い浮かぶように書きます。"
            "キャッチは30字以内、本文は120〜220字、おすすめポイントは3つ、検索キーワードは5つ。"
        ),
        messages=[{"role": "user", "content": f"次の商品の説明文を作ってください。\n\n{facts}"}],
        output_format=Copy,
    )
    if response.stop_reason != "end_turn" or response.parsed_output is None:
        raise RuntimeError(f"{product['商品ID']}: 作成できませんでした（{response.stop_reason}）")
    return response.parsed_output


def check(product: dict, c: dict) -> list[str]:
    issues = []
    if len(c["catch"]) > 30:
        issues.append(f"キャッチが{len(c['catch'])}字（30字以内）")
    if not 120 <= len(c["body"]) <= 220:
        issues.append(f"本文が{len(c['body'])}字（120〜220字）")
    source = " ".join(str(v) for v in product.values()).replace(",", "")
    text = (c["catch"] + c["body"] + "".join(c["points"])).replace(",", "")
    for n in sorted(set(re.findall(r"\d+(?:\.\d+)?", text))):
        if n not in re.findall(r"\d+(?:\.\d+)?", source):
            issues.append(f"商品データにない数字「{n}」")
    for w in NG_WORDS:
        if w in text:
            issues.append(f"誇大な表現「{w}」")
    return issues


def main() -> None:
    sys.stdout.reconfigure(encoding="utf-8")
    ap = argparse.ArgumentParser()
    ap.add_argument("csv")
    ap.add_argument("--from", dest="src", help="作成済みの説明文（JSON）")
    ap.add_argument("--out", default="product_copy.xlsx")
    a = ap.parse_args()
    products = list(csv.DictReader(open(a.csv, encoding="utf-8-sig")))
    done = json.load(open(a.src, encoding="utf-8")) if a.src else {}

    wb = Workbook()
    ws = wb.active
    ws.title = "商品説明"
    ws.append(["商品ID", "商品名", "キャッチコピー", "商品説明", "おすすめポイント", "検索キーワード", "文字数", "チェック"])
    ng = 0
    for p in products:
        c = done.get(p["商品ID"]) or generate(p).model_dump()
        issues = check(p, c)
        ng += bool(issues)
        ws.append([p["商品ID"], p["商品名"], c["catch"], c["body"], "\n".join("・" + x for x in c["points"]),
                   "、".join(c["keywords"]), len(c["body"]), "OK" if not issues else "要確認：" + "／".join(issues)])
    head = PatternFill("solid", fgColor="3B5BDB")
    for cell in ws[1]:
        cell.fill, cell.font = head, Font(bold=True, color="FFFFFF")
    for row in ws.iter_rows(min_row=2):
        for cell in row:
            cell.alignment = Alignment(wrap_text=True, vertical="top")
    for col, w in zip("ABCDEFGH", (8, 24, 30, 60, 32, 30, 7, 24)):
        ws.column_dimensions[col].width = w
    wb.save(a.out)
    print(f"{len(products)}商品 → {a.out}（要確認 {ng} 件）")


if __name__ == "__main__":
    main()
