# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "matplotlib",
#   "requests",
# ]
# ///

"""
日本のGNP（国民総所得 GNI）推移グラフ

- データソース: 世界銀行 World Bank WDI
  NY.GNP.MKTP.CN = GNI (current LCU, 円)
  NY.GNP.MKTP.CD = GNI (current US$)
  API: https://api.worldbank.org/v2/country/JP/indicator/{indicator}?format=json
- PEP 723 準拠: `uv run japan_gnp.py` で実行可能
- オフライン時は組み込みフォールバックデータを使用
- 出力: japan_gnp_trend.png / japan_gnp_trend.pdf

実行:
  uv run japan_gnp.py
  uv run japan_gnp.py --no-fetch   # フォールバックデータのみで描画
  uv run japan_gnp.py --start 1990 --end 2024
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker

# 日本語フォント設定
# 環境にある日本語フォントを優先順に試す（TTF単体を優先：IPA系が最も安定）
_CANDIDATE_FONTS = [
    "IPAexGothic",
    "IPAPGothic",
    "IPAGothic",
    "Noto Sans CJK JP",
    "Noto Sans JP",
    "Noto Serif CJK JP",
    "TakaoPGothic",
    "Takao Pゴシック",
    "TakaoGothic",
    "Hiragino Sans",
    "Yu Gothic",
    "Meiryo",
    "MS Gothic",
    "DejaVu Sans",
]

def setup_japanese_font() -> str:
    from matplotlib.font_manager import findfont, FontProperties

    for name in _CANDIDATE_FONTS:
        try:
            fp = FontProperties(family=name)
            path = findfont(fp, fallback_to_default=False)
            # IPA系など単体TTFは直接 family に指定した方が bold などの
            # weight フォールバックで Liberation に逃げるのを防げる
            matplotlib.rcParams["font.family"] = name
            # sans-serif リストにも先頭に追加（他ライブラリ対策）
            current = matplotlib.rcParams.get("font.sans-serif", [])
            if name not in current:
                matplotlib.rcParams["font.sans-serif"] = [name] + list(current)
            print(f"[font] 使用フォント: {name} ({path})", file=sys.stderr)
            return name
        except Exception:
            continue
    print("[font] 日本語フォントが見つからずデフォルトを使用", file=sys.stderr)
    matplotlib.rcParams["font.family"] = "sans-serif"
    return "DejaVu Sans"


setup_japanese_font()
matplotlib.rcParams["axes.unicode_minus"] = False
# bold が無い IPA でも日本語が欠落しないよう weight を normal に寄せる
matplotlib.rcParams["font.weight"] = "normal"
matplotlib.rcParams["axes.titleweight"] = "normal"

# フォールバックデータ: 世界銀行 NY.GNP.MKTP.CN / CD の取得値をスナップショット
# 取得日 2026-07-13 時点。オフライン実行でもグラフが描けるように 1960-2024 を収録（値は兆円/兆ドル換算前の原数）
FALLBACK_GNI_JPY: dict[int, float] = {
    1960: 16294000000000,
    1961: 19892000000000,
    1962: 21833000000000,
    1963: 24986000000000,
    1965: 33372000000000,
    1970: 75153000000000,
    1975: 152203000000000,
    1980: 251098000000000,
    1985: 330601000000000,
    1990: 443899000000000,
    1995: 521215000000000,
    2000: 535532000000000,
    2005: 535592000000000,
    2010: 508888000000000,
    2011: 503272000000000,
    2012: 512062000000000,
    2013: 524899000000000,
    2014: 535397000000000,
    2015: 553151000000000,
    2016: 553954000000000,
    2017: 573665000000000,
    2018: 577284000000000,
    2019: 581235000000000,
    2020: 568272000000000,
    2021: 599964400000000,
    2022: 619497400000000,
    2023: 651471900000000,
    2024: 672829200000000,
}

FALLBACK_GNI_USD: dict[int, float] = {
    1960: 45200000000,
    1961: 55200000000,
    1970: 209000000000,
    1975: 513000000000,
    1980: 1108000000000,
    1985: 1384000000000,
    1990: 3061000000000,
    1995: 5537000000000,
    2000: 4972000000000,
    2005: 4853000000000,
    2010: 5795000000000,
    2011: 6309000000000,
    2012: 6417000000000,
    2013: 5372000000000,
    2014: 5052000000000,
    2015: 4565000000000,
    2016: 5104000000000,
    2017: 5122000000000,
    2018: 5224000000000,
    2019: 5333000000000,
    2020: 5328000000000,
    2021: 5466430651769,
    2022: 4711073463924,
    2023: 4637104412386,
    2024: 4445039871224,
}

WB_BASE = "https://api.worldbank.org/v2/country/JP/indicator"


def fetch_wb(indicator: str, start: int = 1960, end: int = 2024) -> dict[int, float]:
    """世界銀行APIから取得。失敗時は例外を送出。"""
    import requests

    url = f"{WB_BASE}/{indicator}"
    params = {"format": "json", "per_page": 200, "date": f"{start}:{end}", "source": 2}
    print(f"[fetch] {indicator} {start}-{end} -> {url}", file=sys.stderr)
    r = requests.get(url, params=params, timeout=30)
    r.raise_for_status()
    data = r.json()
    if not isinstance(data, list) or len(data) < 2 or not isinstance(data[1], list):
        raise ValueError(f"unexpected response: {data!r:.500}")
    out: dict[int, float] = {}
    for entry in data[1]:
        try:
            year = int(entry["date"])
            val = entry["value"]
            if val is not None:
                out[year] = float(val)
        except Exception:
            continue
    if not out:
        raise ValueError("no data returned")
    print(f"[fetch] {indicator}: {len(out)} 年分取得 ({min(out)}-{max(out)})", file=sys.stderr)
    return out


def get_data(start: int, end: int, use_fetch: bool) -> tuple[dict[int, float], dict[int, float], str]:
    jpy: dict[int, float] | None = None
    usd: dict[int, float] | None = None
    source = "フォールバック（組み込み）"

    if use_fetch:
        try:
            jpy = fetch_wb("NY.GNP.MKTP.CN", start, end)
            usd = fetch_wb("NY.GNP.MKTP.CD", start, end)
            source = "世界銀行 WDI (World Bank) API - NY.GNP.MKTP.CN / NY.GNP.MKTP.CD"
        except Exception as e:
            print(f"[warn] API取得失敗、フォールバックを使用: {e}", file=sys.stderr)

    if jpy is None or usd is None:
        # フォールバックを期間でフィルタ
        jpy = {y: v for y, v in FALLBACK_GNI_JPY.items() if start <= y <= end}
        usd = {y: v for y, v in FALLBACK_GNI_USD.items() if start <= y <= end}
        # 足りない年は線形補間でなくそのまま欠損として扱うが、
        # フォールバックは疎なので全期間を補うためにAPI失敗時は
        # 可能なら全期間フォールバックをそのまま使う
        if not jpy:
            jpy = dict(FALLBACK_GNI_JPY)
            usd = dict(FALLBACK_GNI_USD)

    return jpy, usd, source


def plot(jpy: dict[int, float], usd: dict[int, float], source: str, start: int, end: int, out_png: Path, out_pdf: Path):
    years_jpy = sorted(jpy)
    vals_jpy = [jpy[y] / 1e12 for y in years_jpy]  # 兆円

    years_usd = sorted(usd)
    vals_usd = [usd[y] / 1e12 for y in years_usd]  # 兆ドル

    # スタイル（この呼び出しが font.rc を上書きするので、後で日本語フォントを再適用）
    plt.style.use("seaborn-v0_8-whitegrid" if "seaborn-v0_8-whitegrid" in plt.style.available else "default")
    # スタイル適用後に日本語フォントを再設定
    setup_japanese_font()
    matplotlib.rcParams["axes.unicode_minus"] = False

    fig, ax1 = plt.subplots(figsize=(13, 7))

    color_jpy = "#0B3D91"  # 紺
    color_usd = "#C53D3D"  # 茜

    # 円建て（左軸）
    ax1.plot(years_jpy, vals_jpy, color=color_jpy, linewidth=2.6, marker="o", markersize=3.5, label="GNI（円建て）")
    ax1.fill_between(years_jpy, vals_jpy, alpha=0.08, color=color_jpy)
    ax1.set_xlabel("年", fontsize=12)
    ax1.set_ylabel("GNI（兆円、名目・円建て）", color=color_jpy, fontsize=12)
    ax1.tick_params(axis="y", labelcolor=color_jpy)
    ax1.set_ylim(bottom=0)

    # ドル建て（右軸）
    ax2 = ax1.twinx()
    ax2.plot(years_usd, vals_usd, color=color_usd, linewidth=2.2, linestyle="--", marker="s", markersize=3, label="GNI（ドル建て）")
    ax2.set_ylabel("GNI（兆ドル、名目・ドル建て）", color=color_usd, fontsize=12)
    ax2.tick_params(axis="y", labelcolor=color_usd)
    ax2.set_ylim(bottom=0)

    # X軸
    ax1.set_xlim(start - 1, end + 1)
    # 目盛りを5年刻みに
    ax1.xaxis.set_major_locator(mticker.MultipleLocator(5))
    ax1.xaxis.set_minor_locator(mticker.MultipleLocator(1))
    plt.setp(ax1.get_xticklabels(), rotation=0)

    # タイトル（IPAは bold が無いため weight は normal のまま、サイズで強調）
    fig.suptitle("日本のGNP（国民総所得 GNI）の推移", fontsize=17, y=0.98)
    ax1.set_title(f"{start}–{end}年 / 名目値  |  出典: {source}", fontsize=9, color="#555555", pad=12)

    # 注記：GNPとGNIの関係
    note = (
        "注: 1993年SNA以降 GNPはGNI（国民総所得）に呼称変更。\n"
        "NY.GNP.MKTP.CN = GNI（円・名目）,  NY.GNP.MKTP.CD = GNI（米ドル・名目）"
    )
    fig.text(0.01, 0.01, note, fontsize=7, color="#666666", va="bottom", ha="left")

    # 最新値の注釈
    if years_jpy:
        ly, lv = years_jpy[-1], vals_jpy[-1]
        ax1.annotate(
            f"{ly}年: {lv:.1f}兆円",
            xy=(ly, lv),
            xytext=(ly - 6, lv + max(vals_jpy) * 0.06),
            arrowprops=dict(arrowstyle="->", color=color_jpy, lw=1.2),
            fontsize=9,
            color=color_jpy,
            bbox=dict(boxstyle="round,pad=0.3", fc="white", ec=color_jpy, alpha=0.9),
        )
    if years_usd:
        ly, lv = years_usd[-1], vals_usd[-1]
        ax2.annotate(
            f"{ly}年: {lv:.2f}兆ドル",
            xy=(ly, lv),
            xytext=(ly - 10, lv + max(vals_usd) * 0.08),
            arrowprops=dict(arrowstyle="->", color=color_usd, lw=1.2),
            fontsize=9,
            color=color_usd,
            bbox=dict(boxstyle="round,pad=0.3", fc="white", ec=color_usd, alpha=0.9),
        )

    # 凡例を統合
    h1, l1 = ax1.get_legend_handles_labels()
    h2, l2 = ax2.get_legend_handles_labels()
    ax1.legend(h1 + h2, l1 + l2, loc="upper left", fontsize=10, framealpha=0.95)

    # グリッド
    ax1.grid(True, which="major", linestyle="--", alpha=0.5)
    ax1.grid(True, which="minor", linestyle=":", alpha=0.2)

    # レイアウト
    fig.tight_layout(rect=[0, 0.03, 1, 0.94])

    fig.savefig(out_png, dpi=200, bbox_inches="tight")
    fig.savefig(out_pdf, bbox_inches="tight")
    print(f"[out] {out_png} ({out_png.stat().st_size / 1024:.1f} KB)", file=sys.stderr)
    print(f"[out] {out_pdf} ({out_pdf.stat().st_size / 1024:.1f} KB)", file=sys.stderr)
    plt.close(fig)


def main():
    p = argparse.ArgumentParser(description="日本のGNP（GNI）推移をグラフ化")
    p.add_argument("--start", type=int, default=1960, help="開始年 (default: 1960)")
    p.add_argument("--end", type=int, default=2024, help="終了年 (default: 2024)")
    p.add_argument("--no-fetch", action="store_true", help="APIを使わずフォールバックデータで描画")
    p.add_argument("--out", type=str, default="japan_gnp_trend.png", help="PNG出力パス")
    p.add_argument("--pdf", type=str, default="japan_gnp_trend.pdf", help="PDF出力パス")
    args = p.parse_args()

    jpy, usd, source = get_data(args.start, args.end, use_fetch=not args.no_fetch)
    print(f"[info] 円建て: {len(jpy)} 年分, ドル建て: {len(usd)} 年分", file=sys.stderr)
    # デバッグ: 最新数年を出力
    for y in sorted(jpy)[-5:]:
        print(f"  {y}: {jpy[y]/1e12:.1f}兆円 / {usd.get(y, 0)/1e12:.2f}兆ドル", file=sys.stderr)

    out_png = Path(args.out)
    out_pdf = Path(args.pdf)
    plot(jpy, usd, source, args.start, args.end, out_png, out_pdf)
    print(f"完了: {out_png.resolve()}", file=sys.stderr)


if __name__ == "__main__":
    main()
