"""PoC (работа A, этап A0): пост-обработка сгенерированного WAV под формат игры.

Вход:  review/<id>_<seed>.wav — AudioLDM2, 16000 Гц, моно, 16-бит.
Выход: review/<id>_<seed>_loop.wav — 22050 Гц, моно 16-бит, нормализованный,
       бесшовный луп (хвост кроссфейдом вплетается в начало — стык без щелчка).

Запуск: ~/.cache/rpg-ai/venv-pxgpt/bin/python tools/aiaudio/post.py review/amb_ponds_1.wav
Проверка стыка: --check прогоняет сшивку twice и ищет скачок на границе.
"""
import sys
import wave
from pathlib import Path

import numpy as np
from scipy.signal import resample_poly

HERE = Path(__file__).resolve().parent
TARGET_RATE = 22050  # формат игры: моно, 22050 Гц, 16-бит (см. audio/gen.mjs)
FADE = 1.0  # длительность кроссфейда лупа, с
PEAK = 0.85  # целевой пик нормализации


def read_wav(path: Path) -> tuple[np.ndarray, int]:
    with wave.open(str(path), "rb") as w:
        rate = w.getframerate()
        data = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16)
    return data.astype(np.float64) / 32768.0, rate


def write_wav(path: Path, samples: np.ndarray) -> None:
    pcm = np.clip(samples * 32767.0, -32768, 32767).astype(np.int16)
    with wave.open(str(path), "wb") as w:
        w.setnchannels(1)
        w.setsampwidth(2)
        w.setframerate(TARGET_RATE)
        w.writeframes(pcm.tobytes())


def loopify(s: np.ndarray, rate: int, fade: float) -> np.ndarray:
    """Хвост (последние fade с) кроссфейдом вплетается в начало — луп без шва.

    B[0..fade] = tail·(1-w) + head·w (хвост затухает, голова нарастает),
    дальше — средина записи; шов (конец -> начало) соседние сэмплы A.
    Длина выхода = len - fade с.
    """
    n = int(fade * rate)
    w = np.linspace(0.0, 1.0, n)
    tail, head, body = s[-n:], s[:n], s[n:-n]
    return np.concatenate([tail * (1 - w) + head * w, body])


def main() -> None:
    args = [a for a in sys.argv[1:] if not a.startswith("--")]
    check = "--check" in sys.argv
    if len(args) != 1:
        print("использование: post.py <review/xxx.wav> [--check]")
        sys.exit(1)
    src = Path(args[0]).resolve()
    s, rate = read_wav(src)
    s = resample_poly(s, TARGET_RATE, rate)  # 16000 -> 22050 (целые множители: 441/320)
    peak = np.abs(s).max()
    if peak > 1e-9:
        s = s * (PEAK / peak)
    s = loopify(s, TARGET_RATE, FADE)
    out = src.with_name(src.stem + "_loop.wav")
    write_wav(out, s)
    dur = s.shape[0] / TARGET_RATE
    print(f"готово: {out.relative_to(HERE)} ({dur:.1f} с, {TARGET_RATE} Гц, пик {PEAK})")

    if check:
        # Стык без щелчка: максимальный межсэмпловый скачок на шве против
        # такого же скачка в середине записи — шов не должен выделяться.
        mid = np.abs(np.diff(s[len(s) // 4 : len(s) // 4 + TARGET_RATE])).max()
        seam = np.abs(s[-1] - s[0])
        print(f"стык: скачок на шве {seam:.4f}, максимум в середине {mid:.4f}")
        assert seam <= max(mid, 0.01), "шов лупа подозрительно громкий"


if __name__ == "__main__":
    main()