#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
jma_download.py  ―  気象庁 震源リスト 月次ダウンローダー
https://www.data.jma.go.jp/eqev/data/daily_map/ から1ヶ月分をまとめて取得する。

使い方:
    python jma_download.py 2026-03          # 2026年3月分をダウンロード
    python jma_download.py 2026-03 --out ./data          # 保存先を指定
    python jma_download.py 2026-03 --delay 2.0           # サーバー間隔を2秒に
    python jma_download.py 2026-03 --no-merge            # 月次統合ファイルを作らない

出力ファイル:
    <out>/20260301.txt  … 日別データ（quake_plot.py で直接読める形式）
    <out>/20260302.txt
    ...
    <out>/202603_all.txt … 月次統合ファイル

データ形式（quake_plot.py の general フォーマット対応）:
    YYYY MM DD HH MM SS.S  lat(°)  lon(°)  depth(km)  M  震央地名

## Yoshio Okamoto with Claude AI on 16 May 2026
"""

import sys
import os
import re
import time
import argparse
import calendar
import urllib.request
import urllib.error
from datetime import date, timedelta

# Windows コンソール UTF-8 強制
if sys.stdout.encoding and sys.stdout.encoding.lower() not in ("utf-8", "utf8"):
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if sys.stderr.encoding and sys.stderr.encoding.lower() not in ("utf-8", "utf8"):
    sys.stderr.reconfigure(encoding="utf-8", errors="replace")

# ============================================================
# 設定
# ============================================================

BASE_URL    = "https://www.data.jma.go.jp/eqev/data/daily_map/{date}.html"
USER_AGENT  = "Mozilla/5.0 (compatible; JMA-downloader/1.0)"
DEFAULT_DELAY = 1.5   # リクエスト間隔（秒）。サーバーへの配慮として変えないこと

# ============================================================
# HTML 取得
# ============================================================

def fetch_html(date_str: str) -> str | None:
    """
    YYYYMMDD 形式の日付文字列で1日分の HTML を取得する。
    失敗時は None を返す。
    """
    url = BASE_URL.format(date=date_str)
    req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            raw = resp.read()
            # 気象庁ページは UTF-8
            return raw.decode("utf-8", errors="replace")
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print(f"  [スキップ] {date_str}: データなし (404)")
        else:
            print(f"  [エラー]   {date_str}: HTTP {e.code}")
        return None
    except urllib.error.URLError as e:
        print(f"  [エラー]   {date_str}: {e.reason}")
        return None
    except Exception as e:
        print(f"  [エラー]   {date_str}: {e}")
        return None

# ============================================================
# 座標パーサー
# ============================================================

# 例: 25°17.4'N  /  124°55.9'E
_LAT_RE = re.compile(r"(\d+)°\s*([\d.]+)'([NS])")
_LON_RE = re.compile(r"(\d+)°\s*([\d.]+)'([EW])")

def parse_coord(lat_str: str, lon_str: str) -> tuple[float, float] | None:
    """
    '25°17.4'N' '124°55.9'E' → (25.29, 124.932) のように十進度へ変換する。
    """
    m_lat = _LAT_RE.search(lat_str)
    m_lon = _LON_RE.search(lon_str)
    if not m_lat or not m_lon:
        return None
    lat = int(m_lat.group(1)) + float(m_lat.group(2)) / 60.0
    lon = int(m_lon.group(1)) + float(m_lon.group(2)) / 60.0
    if m_lat.group(3) == "S":
        lat = -lat
    if m_lon.group(3) == "W":
        lon = -lon
    return lat, lon

# ============================================================
# HTML → 震源レコードのパース
# ============================================================

# 震源リスト1行のパターン
# 例: 2026  3  1 00:00 46.9  25°17.4'N 124°55.9'E    0     2.2  宮古島近海
#     2026  3  1 00:18 18.4  25°19.7'N 124°55.8'E   12     -    宮古島近海  ← M が "-" の場合あり
_LINE_RE = re.compile(
    r"(\d{4})\s+(\d{1,2})\s+(\d{1,2})\s+"   # 年 月 日
    r"(\d{2}):(\d{2})\s+([\d.]+)\s+"          # 時:分 秒
    r"(\d+°\s*[\d.]+'\s*[NS])\s+"             # 緯度
    r"(\d+°\s*[\d.]+'\s*[EW])\s+"             # 経度
    r"(\d+|-)\s+"                              # 深さ
    r"([-\d.]+|-)\s+"                          # マグニチュード
    r"(.+)"                                    # 震央地名
)

def parse_html(html: str) -> list[str]:
    """
    HTML から震源データ行を抽出し、quake_plot.py の general 形式に変換して返す。

    出力形式（空白区切り）:
        YYYY MM DD HH MM SS.S  lat  lon  depth  M  震央地名
    """
    records = []
    for line in html.splitlines():
        m = _LINE_RE.search(line)
        if not m:
            continue
        yr, mo, dy = m.group(1), m.group(2).zfill(2), m.group(3).zfill(2)
        hh, mi, sc = m.group(4), m.group(5), m.group(6)
        lat_str, lon_str = m.group(7), m.group(8)
        depth_str = m.group(9)
        mag_str   = m.group(10)
        place     = m.group(11).strip()

        # 座標変換
        coords = parse_coord(lat_str, lon_str)
        if coords is None:
            continue
        lat, lon = coords

        # 深さ・マグニチュードが "-" の場合は欠測として除外
        # （quake_plot.py でも読み飛ばされるが、明示的に除く）
        if depth_str == "-":
            depth_str = "0"    # 深さ不明 → 0 として保持
        if mag_str == "-":
            continue           # M 不明は除外（プロット不可のため）

        records.append(
            f"{yr} {mo} {dy} {hh} {mi} {sc:>6}  "
            f"{lat:9.4f}  {lon:10.4f}  {depth_str:>4}  {mag_str:>5}  {place}"
        )
    return records

# ============================================================
# 1日分の取得・保存
# ============================================================

def download_day(d: date, out_dir: str) -> list[str]:
    """
    1日分をダウンロードし、ファイルに保存する。
    保存したレコードのリストを返す（月次統合用）。
    """
    date_str  = d.strftime("%Y%m%d")
    out_path  = os.path.join(out_dir, f"{date_str}.txt")

    # すでに存在する場合はスキップ
    if os.path.exists(out_path):
        print(f"  [スキップ] {date_str}: ファイル既存 ({out_path})")
        with open(out_path, encoding="utf-8") as f:
            return [l.rstrip() for l in f if not l.startswith("#") and l.strip()]

    print(f"  [取得中]   {date_str} ...", end="", flush=True)
    html = fetch_html(date_str)
    if html is None:
        return []

    records = parse_html(html)
    print(f" {len(records)} 件")

    if records:
        header = (
            f"# 気象庁 震源リスト (暫定値)  {d.strftime('%Y年%m月%d日')}\n"
            f"# YYYY MM DD HH MM SS.S  lat(deg)  lon(deg)  depth(km)  M  震央地名\n"
        )
        with open(out_path, "w", encoding="utf-8") as f:
            f.write(header)
            f.write("\n".join(records) + "\n")

    return records

# ============================================================
# 月次統合ファイル作成
# ============================================================

def write_monthly(year: int, month: int, all_records: list[str], out_dir: str):
    """全日分のレコードを1ファイルに統合する。"""
    out_path = os.path.join(out_dir, f"{year:04d}{month:02d}_all.txt")
    header = (
        f"# 気象庁 震源リスト (暫定値)  {year}年{month:02d}月 統合\n"
        f"# YYYY MM DD HH MM SS.S  lat(deg)  lon(deg)  depth(km)  M  震央地名\n"
    )
    with open(out_path, "w", encoding="utf-8") as f:
        f.write(header)
        f.write("\n".join(all_records) + "\n")
    print(f"\n[完了] 統合ファイル: {out_path}  ({len(all_records)} 件)")

# ============================================================
# メイン
# ============================================================

def main():
    parser = argparse.ArgumentParser(
        description="気象庁 震源リストを月単位で一括ダウンロードする",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""\
例:
  python jma_download.py 2026-03
  python jma_download.py 2025-01 --out ./data/2025
  python jma_download.py 2024-12 --delay 2.0 --no-merge

quake_plot.py での使い方:
  python quake_plot.py data/20260301.txt --format general --date 2026-03-01
  python quake_plot.py data/202603_all.txt --format general --date 2026-03-15
""")
    parser.add_argument("yearmonth",
                        help="対象年月 (YYYY-MM 形式)  例: 2026-03")
    parser.add_argument("--out", default=None,
                        help="保存先ディレクトリ (デフォルト: YYYY/MM)")
    parser.add_argument("--delay", type=float, default=DEFAULT_DELAY,
                        help=f"リクエスト間隔 秒 (デフォルト: {DEFAULT_DELAY})")
    parser.add_argument("--no-merge", action="store_true",
                        help="月次統合ファイルを作成しない")

    args = parser.parse_args()

    # 年月の解析
    try:
        year, month = map(int, args.yearmonth.split("-"))
        if not (1 <= month <= 12):
            raise ValueError
    except ValueError:
        print("[エラー] yearmonth は YYYY-MM 形式で指定してください (例: 2026-03)")
        sys.exit(1)

    # 保存先ディレクトリの作成（デフォルト: YYYY/MM）
    out_dir = args.out if args.out is not None else os.path.join(f"{year:04d}", f"{month:02d}")
    os.makedirs(out_dir, exist_ok=True)

    # 対象日リスト
    _, last_day = calendar.monthrange(year, month)
    days = [date(year, month, d) for d in range(1, last_day + 1)]
    # 未来の日付は除外
    today = date.today()
    days  = [d for d in days if d <= today]

    print(f"[開始] {year}年{month:02d}月  {len(days)}日分  → {out_dir}")
    print(f"       リクエスト間隔: {args.delay} 秒\n")

    all_records = []
    for i, d in enumerate(days):
        recs = download_day(d, out_dir)
        all_records.extend(recs)
        # 最後以外はウェイト
        if i < len(days) - 1:
            time.sleep(args.delay)

    # 月次統合ファイル
    if not args.no_merge and all_records:
        write_monthly(year, month, all_records, out_dir)

    total = len(all_records)
    print(f"\n[完了] 合計 {total} 件")


if __name__ == "__main__":
    main()
