"""ネットショップの注文データ（1年分）を分析し、グラフと数字をまとめる（制作サンプル）。

    python analyze.py            # make_data() で架空の注文データを作り、分析して charts/ と summary.json を出力
"""

import csv
import json
import random
from collections import Counter, defaultdict
from datetime import date, datetime, timedelta
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import font_manager

HERE = Path(__file__).parent
font_manager.fontManager.addfont("C:/Windows/Fonts/YuGothM.ttc")
plt.rcParams["font.family"] = font_manager.FontProperties(fname="C:/Windows/Fonts/YuGothM.ttc").get_name()
ACCENT, GRAY, SUB = "#3b5bdb", "#98a2b3", "#f59f00"

PRODUCTS = [  # 商品, カテゴリ, 価格
    ("ドリップバッグ 10袋", "コーヒー", 1280), ("深煎りブレンド 200g", "コーヒー", 1620), ("季節のブレンド 200g", "コーヒー", 1780),
    ("カフェインレス 200g", "コーヒー", 1880), ("ほうじ茶ティーバッグ", "お茶", 980), ("和紅茶 50g", "お茶", 1320),
    ("焼き菓子アソート", "お菓子", 2160), ("チーズクッキー", "お菓子", 860), ("ギフトボックス S", "ギフト", 3240),
    ("ギフトボックス L", "ギフト", 5400), ("ステンレスマグ", "雑貨", 2200), ("コーヒーミル", "雑貨", 5980),
]


def make_data(path: Path) -> None:
    """架空の注文データ（2025年10月〜2026年9月）を作る"""
    random.seed(3)
    customers, rows, oid = [], [], 1000
    d = date(2025, 10, 1)
    while d <= date(2026, 9, 30):
        season = {11: 1.25, 12: 1.75, 1: 1.0, 2: 1.05, 3: 1.0, 4: 0.9, 5: 0.95, 6: 0.85, 7: 0.8, 8: 0.85, 9: 0.95, 10: 1.0}[d.month]
        growth = 1 + (d - date(2025, 10, 1)).days / 365 * 0.25
        n = max(0, int(random.gauss(8.5 * season * growth * (1.25 if d.weekday() >= 5 else 1), 2.5)))
        for _ in range(n):
            if customers and random.random() < 0.38:
                cid = random.choice(customers)
            else:
                cid = f"C{len(customers) + 1:04d}"
                customers.append(cid)
            hour = random.choices(range(24), weights=[1, 1, 0, 0, 0, 1, 2, 4, 5, 4, 4, 5, 7, 6, 4, 4, 4, 5, 6, 8, 10, 11, 9, 4])[0]
            oid += 1
            k = random.choices([1, 2, 3], weights=[55, 32, 13])[0]
            gift_boost = 4 if d.month == 12 else 1
            for name, cat, price in random.choices(PRODUCTS, weights=[14, 12, 8, 5, 7, 5, 6, 6, 3 * gift_boost, 2 * gift_boost, 3, 1], k=k):
                rows.append([f"O{oid}", d.isoformat(), f"{hour:02d}", cid, name, cat, price, random.choice([1, 1, 1, 2])])
        d += timedelta(days=1)
    with open(path, "w", encoding="utf-8-sig", newline="") as f:
        w = csv.writer(f)
        w.writerow(["注文番号", "注文日", "時", "顧客ID", "商品", "カテゴリ", "単価", "数量"])
        w.writerows(rows)


def analyze(path: Path) -> dict:
    rows = list(csv.DictReader(open(path, encoding="utf-8-sig")))
    for r in rows:
        r["金額"] = int(r["単価"]) * int(r["数量"])
    orders = defaultdict(lambda: {"金額": 0})
    for r in rows:
        o = orders[r["注文番号"]]
        o["金額"] += r["金額"]
        o["日"], o["顧客"], o["時"] = r["注文日"], r["顧客ID"], int(r["時"])
    month = defaultdict(int)
    for o in orders.values():
        month[o["日"][:7]] += o["金額"]
    cat = Counter()
    prod = Counter()
    for r in rows:
        cat[r["カテゴリ"]] += r["金額"]
        prod[r["商品"]] += r["金額"]
    wd = defaultdict(int)
    hour = Counter()
    for o in orders.values():
        wd[datetime.fromisoformat(o["日"]).weekday()] += o["金額"]
        hour[o["時"]] += 1
    # リピート：顧客ごとの注文回数
    per_cust = Counter(o["顧客"] for o in orders.values())
    repeat_customers = sum(1 for c in per_cust.values() if c >= 2)
    sales_by_cust = defaultdict(int)
    for o in orders.values():
        sales_by_cust[o["顧客"]] += o["金額"]
    repeat_sales = sum(v for k, v in sales_by_cust.items() if per_cust[k] >= 2)
    total = sum(o["金額"] for o in orders.values())
    # ABC分析（売上上位の商品で何%を占めるか）
    cum, abc = 0, []
    for name, v in prod.most_common():
        cum += v
        abc.append((name, v, round(cum / total * 100, 1)))
    months = sorted(month)
    h1 = sum(month[m] for m in months[:6])
    h2 = sum(month[m] for m in months[6:])
    return {
        "total": total, "orders": len(orders), "customers": len(per_cust),
        "aov": round(total / len(orders)), "repeat_rate": round(repeat_customers / len(per_cust) * 100, 1),
        "repeat_sales_share": round(repeat_sales / total * 100, 1),
        "month": {m: month[m] for m in months}, "cat": dict(cat.most_common()),
        "weekday": [wd[i] for i in range(7)], "hour": [hour[i] for i in range(24)],
        "abc": abc, "h2_vs_h1": round((h2 / h1 - 1) * 100, 1),
        "dec_share": round(month["2025-12"] / total * 100, 1),
        "gift_dec": sum(r["金額"] for r in rows if r["カテゴリ"] == "ギフト" and r["注文日"].startswith("2025-12")),
        "gift_total": cat["ギフト"],
        "evening_share": round(sum(hour[h] for h in range(19, 23)) / len(orders) * 100, 1),
        "weekend_avg_vs_weekday": round(((wd[5] + wd[6]) / 2) / (sum(wd[i] for i in range(5)) / 5) * 100 - 100, 1),
    }


def charts(s: dict) -> None:
    out = HERE / "charts"
    out.mkdir(exist_ok=True)

    def style(ax):
        ax.spines[["top", "right"]].set_visible(False)
        ax.grid(axis="y", color="#e4e7ec", lw=.8)
        ax.set_axisbelow(True)

    # 1. 月別売上
    fig, ax = plt.subplots(figsize=(8, 3.4), dpi=150)
    m = list(s["month"])
    vals = [v / 10000 for v in s["month"].values()]
    ax.bar([x[2:].replace("-", "/") for x in m], vals, color=[SUB if x == "2025-12" else ACCENT for x in m])
    ax.set_ylabel("売上（万円）")
    style(ax)
    for i, v in enumerate(vals):
        ax.text(i, v + max(vals) * .015, f"{v:.0f}", ha="center", fontsize=8, color="#475467")
    fig.tight_layout(); fig.savefig(out / "monthly.png"); plt.close(fig)

    # 2. カテゴリ別（横棒）
    fig, ax = plt.subplots(figsize=(4.6, 3.2), dpi=150)
    cats = list(s["cat"])[::-1]
    ax.barh(cats, [s["cat"][c] / 10000 for c in cats], color=ACCENT)
    ax.set_xlabel("売上（万円）")
    ax.spines[["top", "right"]].set_visible(False)
    fig.tight_layout(); fig.savefig(out / "category.png"); plt.close(fig)

    # 3. 時間帯別の注文数
    fig, ax = plt.subplots(figsize=(4.6, 3.2), dpi=150)
    ax.bar(range(24), s["hour"], color=[SUB if 19 <= h <= 22 else GRAY for h in range(24)])
    ax.set_xticks(range(0, 24, 3)); ax.set_xlabel("注文された時刻（時）"); ax.set_ylabel("注文数")
    style(ax)
    fig.tight_layout(); fig.savefig(out / "hour.png"); plt.close(fig)

    # 4. ABC分析（累積構成比）
    fig, ax = plt.subplots(figsize=(8, 3.2), dpi=150)
    names = [a[0] for a in s["abc"]]
    ax.bar(range(len(names)), [a[1] / 10000 for a in s["abc"]], color=[ACCENT if a[2] <= 80 or i == 0 else GRAY for i, a in enumerate(s["abc"])])
    ax.set_xticks(range(len(names)), names, rotation=35, ha="right", fontsize=8)
    ax.set_ylabel("売上（万円）")
    ax2 = ax.twinx()
    ax2.plot(range(len(names)), [a[2] for a in s["abc"]], color=SUB, marker="o", ms=3)
    ax2.axhline(80, color=SUB, ls="--", lw=.8)
    ax2.set_ylim(0, 105); ax2.set_ylabel("累積構成比（%）")
    style(ax)
    ax2.spines[["top"]].set_visible(False)
    fig.tight_layout(); fig.savefig(out / "abc.png"); plt.close(fig)


if __name__ == "__main__":
    data = HERE / "orders.csv"
    make_data(data)
    s = analyze(data)
    charts(s)
    (HERE / "summary.json").write_text(json.dumps(s, ensure_ascii=False, indent=1), encoding="utf-8")
    print(json.dumps({k: v for k, v in s.items() if k not in ("month", "hour", "abc", "weekday")}, ensure_ascii=False))
