#!/usr/bin/env python3
"""tide_jiai.py — 潮位データから「潮が動く時間帯」を可視化する

入力(どちらか):
    (A) 気象庁 潮位表テキストデータ版 (毎時潮位、満潮・干潮の時刻と潮位)
        https://www.jma.go.jp の潮位表ページからダウンロードした年間ファイル
    (B) CSV (2列: datetime, level_cm)

出力:
    - tide_jiai.html : 潮位と潮位変化速度のインタラクティブグラフ (Plotly)
    - 標準出力に「夜間で潮がよく動く時間帯」ランキング

使い方:
    python tide_jiai.py --jma D3.txt --start 2026-07-22 --days 4
    python tide_jiai.py --csv tide.csv
    python tide_jiai.py --demo   # 合成データでデモ実行
"""

import argparse
import sys
from datetime import datetime

import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

NIGHT_HOURS = set(range(18, 24)) | set(range(0, 5))  # 18時〜翌4時を「夜間」とする
DAY_HOURS = set(range(5, 18))                        # 5時〜17時を「昼間」とする
WEEKDAYS_JA = ["月", "火", "水", "木", "金", "土", "日"]


def load_jma_text(path: str, century: int = 2000) -> pd.DataFrame:
    """気象庁 潮位表テキストデータ版(毎時潮位)を読み込む。

    フォーマット(1行=1日, 固定長):
        1-72   カラム: 毎時潮位 3桁×24時間 (0〜23時) [cm]
        73-78  カラム: 年月日 2桁×3 (西暦下2桁, 月, 日)
        79-80  カラム: 地点記号
        81-136 カラム: 満潮・干潮の時刻と潮位 (本スクリプトでは未使用)
    """
    records = []
    with open(path, encoding="ascii") as f:
        for line in f:
            line = line.rstrip("\n")
            if len(line) < 80:
                continue
            yy = int(line[72:74])
            mm = int(line[74:76])
            dd = int(line[76:78])
            date = datetime(century + yy, mm, dd)
            for h in range(24):
                level = int(line[h * 3 : h * 3 + 3])
                records.append(
                    {"datetime": date + pd.Timedelta(hours=h), "level_cm": level}
                )
    df = pd.DataFrame(records)
    station = None
    with open(path, encoding="ascii") as f:
        first = f.readline()
        if len(first) >= 80:
            station = first[78:80].strip()
    if station:
        print(f"地点記号: {station} / 期間: {df['datetime'].min():%Y-%m-%d} 〜 "
              f"{df['datetime'].max():%Y-%m-%d} ({len(df)} 時間分)")
    return df.sort_values("datetime").reset_index(drop=True)


def load_csv(path: str) -> pd.DataFrame:
    df = pd.read_csv(path, names=["datetime", "level_cm"], header=0)
    df["datetime"] = pd.to_datetime(df["datetime"])
    return df.sort_values("datetime").reset_index(drop=True)


def make_demo_data(days: int = 3) -> pd.DataFrame:
    """主要4分潮(M2, S2, K1, O1)を合成した架空の潮位データを生成する。"""
    t = pd.date_range("2026-07-22 00:00", periods=24 * days + 1, freq="h")
    hours = np.arange(len(t), dtype=float)
    level = (
        100
        + 60 * np.cos(2 * np.pi * hours / 12.42)
        + 25 * np.cos(2 * np.pi * hours / 12.00 + 0.8)
        + 20 * np.cos(2 * np.pi * hours / 23.93 + 1.5)
        + 15 * np.cos(2 * np.pi * hours / 25.82 + 2.2)
    )
    return pd.DataFrame({"datetime": t, "level_cm": level.round(1)})


def clip_window(df: pd.DataFrame, start: str | None, days: int | None) -> pd.DataFrame:
    if start:
        t0 = pd.Timestamp(start)
        df = df[df["datetime"] >= t0]
    if days:
        t0 = df["datetime"].iloc[0]
        df = df[df["datetime"] < t0 + pd.Timedelta(days=days)]
    return df.reset_index(drop=True)


def analyze(df: pd.DataFrame) -> pd.DataFrame:
    """潮位変化速度 rate_cm_h [cm/h] を中央差分で計算する。"""
    hours = (df["datetime"] - df["datetime"].iloc[0]).dt.total_seconds() / 3600
    df = df.copy()
    df["rate_cm_h"] = np.gradient(df["level_cm"].to_numpy(dtype=float), hours.to_numpy())
    df["abs_rate"] = df["rate_cm_h"].abs()
    df["is_night"] = df["datetime"].dt.hour.isin(NIGHT_HOURS)
    df["is_day"] = df["datetime"].dt.hour.isin(DAY_HOURS)
    return df


def ranking(df: pd.DataFrame, mask_col: str, top_n: int = 8) -> pd.DataFrame:
    sel = df[df[mask_col]].sort_values("abs_rate", ascending=False)
    cols = ["datetime", "level_cm", "rate_cm_h"]
    return sel.head(top_n)[cols]


def print_ranking(df: pd.DataFrame, mask_col: str, label: str) -> None:
    print(f"\n== {label}で潮がよく動く時間帯 TOP8 ==")
    for _, r in ranking(df, mask_col).iterrows():
        wd = WEEKDAYS_JA[r["datetime"].weekday()]
        arrow = "↑上げ" if r["rate_cm_h"] > 0 else "↓下げ"
        print(f"{r['datetime']:%m/%d} ({wd}) {r['datetime']:%H:%M}  "
              f"潮位 {r['level_cm']:6.1f} cm  {arrow} {abs(r['rate_cm_h']):5.1f} cm/h")


def plot(df: pd.DataFrame, out_html: str, title: str = "") -> None:
    fig = make_subplots(
        rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.08,
        subplot_titles=("潮位 [cm]", "潮位変化速度 [cm/h](絶対値が大きいほど潮が動いている)"),
    )
    fig.add_trace(
        go.Scatter(x=df["datetime"], y=df["level_cm"], name="潮位",
                   line=dict(color="#12293d", width=2)),
        row=1, col=1,
    )
    fig.add_trace(
        go.Scatter(x=df["datetime"], y=df["rate_cm_h"], name="変化速度",
                   line=dict(color="#d98a12", width=2)),
        row=2, col=1,
    )
    fig.add_hline(y=0, line=dict(color="#8aa0b0", width=1, dash="dot"), row=2, col=1)

    night_blocks = []
    in_night = False
    for _, r in df.iterrows():
        if r["is_night"] and not in_night:
            start = r["datetime"]; in_night = True
        elif not r["is_night"] and in_night:
            night_blocks.append((start, r["datetime"])); in_night = False
    if in_night:
        night_blocks.append((start, df["datetime"].iloc[-1]))
    for s, e in night_blocks:
        fig.add_vrect(x0=s, x1=e, fillcolor="#12293d", opacity=0.08, line_width=0)

    fig.update_layout(
        template="plotly_white", height=580,
        title=dict(text=title, y=0.98) if title else None,
        margin=dict(l=50, r=20, t=80 if title else 50, b=60),
        legend=dict(orientation="h", yanchor="top", y=-0.08, x=0),
        font=dict(family="Noto Sans JP, sans-serif"),
    )
    fig.write_html(out_html, include_plotlyjs="cdn")
    print(f"グラフを書き出しました: {out_html}")


def main() -> None:
    p = argparse.ArgumentParser()
    p.add_argument("--jma", help="気象庁 潮位表テキストデータ版のファイル")
    p.add_argument("--csv", help="潮位CSV (datetime, level_cm)")
    p.add_argument("--demo", action="store_true", help="合成データでデモ実行")
    p.add_argument("--start", help="解析開始日 (例 2026-07-22)")
    p.add_argument("--days", type=int, help="解析日数 (例 4)")
    p.add_argument("-o", "--out", default="tide_jiai.html")
    p.add_argument("--title", default="", help="グラフのタイトル")
    args = p.parse_args()

    if args.jma:
        df = load_jma_text(args.jma)
    elif args.csv:
        df = load_csv(args.csv)
    elif args.demo:
        df = make_demo_data()
    else:
        p.print_help(); sys.exit(1)

    df = clip_window(df, args.start, args.days)
    df = analyze(df)
    plot(df, args.out, args.title)

    print_ranking(df, "is_night", "夜間(18時〜翌4時)")
    print_ranking(df, "is_day", "昼間(5時〜17時)")


if __name__ == "__main__":
    main()
