#!/usr/bin/env python3
"""Учебный монтаж ролика: интервал 1-5 с, титр «Учебный пример», MP4 640x360.

Библиотека отрисовки титра: Pillow (PIL). Внешняя программа кодирования: ffmpeg/libx264.
Исходный файл не изменяется.
"""
from __future__ import annotations

import json
import shlex
import subprocess
import sys
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont

ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "маршруты" / "медиа" / "учебный-ролик.mp4"
OUT_DIR = ROOT / "результаты"
OUT = OUT_DIR / "учебный-ролик-титр-640x360.mp4"
TITLE_PNG = ROOT / "сборка" / "титр-учебный-пример.png"
LOG = ROOT / "сборка" / "видео-команды.json"

START, END = 1.0, 5.0          # вырезаемый интервал, с
WIDTH, HEIGHT, FPS = 640, 360, 25
TEXT = "Учебный пример"
FONT_PATH = "/System/Library/Fonts/Supplemental/Verdana Bold.ttf"
FONT_SIZE = 28
BAND_H = 56                    # высота плашки титра, px
BAND_ALPHA = 140               # 140/255 ~ 0.55 непрозрачности чёрной плашки
BAND_RGB = (0, 0, 0)
TEXT_RGB = (255, 255, 255)


def ffmpeg_version() -> str:
    out = subprocess.run(["ffmpeg", "-version"], capture_output=True, text=True, check=True)
    return out.stdout.splitlines()[0]


def make_title_png() -> dict:
    """Отрисовать титр в PNG с альфа-каналом: плашка внизу кадра + белый текст."""
    img = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
    draw.rectangle([0, HEIGHT - BAND_H, WIDTH, HEIGHT], fill=(*BAND_RGB, BAND_ALPHA))
    font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
    draw.text((WIDTH / 2, HEIGHT - BAND_H / 2), TEXT, font=font,
              fill=(*TEXT_RGB, 255), anchor="mm")
    TITLE_PNG.parent.mkdir(parents=True, exist_ok=True)
    img.save(TITLE_PNG)

    # Маска глифов: пиксели плашки, ставшие почти непрозрачными (сам текст).
    alpha = img.getchannel("A").load()
    glyphs = [(x, y) for y in range(HEIGHT - BAND_H, HEIGHT) for x in range(WIDTH)
              if alpha[x, y] >= 200]
    return {
        "title_png": str(TITLE_PNG.relative_to(ROOT)),
        "font": FONT_PATH,
        "font_size": FONT_SIZE,
        "band": {"y_from": HEIGHT - BAND_H, "y_to": HEIGHT, "alpha": BAND_ALPHA},
        "glyph_pixels": len(glyphs),
        "glyph_bbox": [min(p[0] for p in glyphs), min(p[1] for p in glyphs),
                       max(p[0] for p in glyphs), max(p[1] for p in glyphs)],
    }


def build_ffmpeg_cmd() -> list[str]:
    """Вырезать 1-5 с, привести к 640x360 и наложить титр; звук не переносится."""
    filter_complex = (
        f"[0:v]trim=start={START}:end={END},setpts=PTS-STARTPTS,"
        f"scale={WIDTH}:{HEIGHT}:flags=lanczos,setsar=1,format=rgba[base];"
        f"[base][1:v]overlay=0:0:format=auto,format=yuv420p[out]"
    )
    return [
        "ffmpeg", "-y", "-hide_banner", "-v", "error",
        "-i", str(SRC),
        "-i", str(TITLE_PNG),
        "-filter_complex", filter_complex,
        "-map", "[out]",
        "-frames:v", "100",
        "-r", str(FPS),
        "-c:v", "libx264", "-preset", "medium", "-crf", "18",
        "-pix_fmt", "yuv420p", "-movflags", "+faststart",
        "-an",
        str(OUT),
    ]


def main() -> int:
    if not SRC.is_file():
        print(f"нет исходного файла: {SRC}", file=sys.stderr)
        return 1
    OUT_DIR.mkdir(parents=True, exist_ok=True)

    title = make_title_png()
    cmd = build_ffmpeg_cmd()
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        print(proc.stderr, file=sys.stderr)
        return proc.returncode

    log = {
        "задача": "интервал 1-5 с, титр «Учебный пример», MP4 640x360",
        "исходный_файл": str(SRC.relative_to(ROOT)),
        "результат": str(OUT.relative_to(ROOT)),
        "библиотеки": {
            "Pillow (PIL)": __import__("PIL").__version__,
            "Python": sys.version.split()[0],
        },
        "внешние_программы": {
            "ffmpeg": ffmpeg_version(),
            "кодек": "libx264 (H.264), pixel format yuv420p",
        },
        "титр": title,
        "команды": [
            " ".join(shlex.quote(a) for a in cmd),
            " ".join(shlex.quote(a) for a in ["ffprobe", "-v", "error", "-print_format",
                                              "json", "-show_format", "-show_streams", str(OUT)]),
        ],
    }
    LOG.write_text(json.dumps(log, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(f"готово: {OUT} ({OUT.stat().st_size} байт)")
    print(f"журнал: {LOG}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
