"""
TikTok-Clip: legal stock runterladen → leicht verändern → in video-pool / Upload.

Nur freie Stock-Quellen oder eigene Rechte. Kein 1:1-Reupload fremder TikToks ohne Erlaubnis.
"""

from __future__ import annotations

import os
import random
import re
import shutil
import subprocess
import time
import webbrowser
from datetime import datetime
from pathlib import Path
from urllib.parse import quote_plus, urlparse

from jam.brain import JamBrain, JamState
from jam.owner import owner_name

ROOT = Path(__file__).resolve().parent.parent
REMIX_SRC = Path.home() / "Documents" / "Jam-TikTok-Posten" / "remix-quelle"
REMIX_OUT = Path.home() / "Documents" / "Jam-TikTok-Posten" / "remix-fertig"
CLIPS_READY = Path.home() / "Documents" / "Jam-TikTok-Posten" / "clips-ready"

MAX_BYTES = 80_000_000

# Direkte MP4-Hosts (Stock/CDN)
ALLOWED_VIDEO_HOSTS = (
    "mixkit.co",
    "assets.mixkit.co",
    "pexels.com",
    "videos.pexels.com",
    "pixabay.com",
    "cdn.pixabay.com",
    "coverr.co",
    "storage.coverr.co",
    "wikimedia.org",
    "upload.wikimedia.org",
    "w3schools.com",
    "download.samplelib.com",
    "samplelib.com",
)

BLOCKED_HOSTS = (
    "tiktok.com",
    "instagram.com",
    "facebook.com",
    "youtube.com",
    "youtu.be",
)

# Kuratierte Mixkit-Previews (Social/Media – frei laut Mixkit-Lizenz)
MIXKIT_SAMPLES: tuple[str, ...] = (
    "https://assets.mixkit.co/videos/preview/mixkit-hands-holding-a-smartphone-with-the-screen-on-3456-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-woman-doing-social-media-marketing-4065-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-man-under-neon-lights-1232-large.mp4",
    "https://assets.mixkit.co/videos/preview/mixkit-young-woman-vlogging-with-her-smartphone-4070-large.mp4",
)

# Fallback wenn Mixkit/Pexels blockiert (403)
FALLBACK_CLIPS: tuple[str, ...] = (
    "https://download.samplelib.com/mp4/sample-5s.mp4",
    "https://www.w3schools.com/html/mov_bbb.mp4",
)

# Pexels CDN (Referer nötig) – kuratiert, 9:16 / cinematic, royalty-free [Pexels License](https://www.pexels.com/license/)
CURATED_PEXELS_CLIPS: tuple[tuple[str, str], ...] = (
    (
        "portrait_neon_city",
        "https://videos.pexels.com/video-files/7197655/7197655-uhd_1440_2560_25fps.mp4",
    ),
    (
        "portrait_creator",
        "https://videos.pexels.com/video-files/2499611/2499611-hd_1080_1920_30fps.mp4",
    ),
    (
        "cinematic_city_night",
        "https://videos.pexels.com/video-files/3195393/3195393-uhd_2560_1440_25fps.mp4",
    ),
    (
        "cinematic_glow",
        "https://videos.pexels.com/video-files/3195396/3195396-uhd_2560_1440_25fps.mp4",
    ),
    (
        "aerial_city_4k",
        "https://videos.pexels.com/video-files/3195394/3195394-uhd_3840_2160_25fps.mp4",
    ),
    (
        "social_b_roll",
        "https://videos.pexels.com/video-files/3254067/3254067-uhd_2560_1440_25fps.mp4",
    ),
)

# Abwechslung für neue Posts (Mixkit-Reihenfolge + Pexels-Suche)
FRESH_STOCK_QUERIES: tuple[str, ...] = (
    "content creator filming smartphone portrait",
    "social media marketing influencer",
    "woman vlogging smartphone neon",
    "young creator recording video phone",
    "digital marketing team smartphone",
)

STOCK_SEARCH = (
    ("Mixkit", "https://mixkit.co/free-stock-video/{q}/"),
    ("Pexels", "https://www.pexels.com/search/videos/{q}/"),
    ("Pixabay", "https://pixabay.com/videos/search/{q}/"),
    ("Coverr", "https://coverr.co/de/suche?q={q}"),
)


def _host_allowed(url: str) -> bool:
    host = (urlparse(url).netloc or "").lower()
    if any(b in host for b in BLOCKED_HOSTS):
        return os.environ.get("JAM_REMIX_I_HAVE_RIGHTS", "").strip().lower() in (
            "1",
            "true",
            "yes",
        )
    return any(a in host for a in ALLOWED_VIDEO_HOSTS) or url.lower().endswith((".mp4", ".mov", ".webm"))


def _find_ffmpeg() -> str | None:
    for name in ("ffmpeg", "ffmpeg.exe"):
        p = shutil.which(name)
        if p:
            return p
    for base in (
        Path.home() / "Documents" / "Jam-Tools",
        ROOT / "tools",
    ):
        exe = base / "ffmpeg.exe"
        if exe.is_file():
            return str(exe)
    return None


def download_video_url(url: str, dest: Path) -> tuple[bool, str]:
    if not _host_allowed(url):
        return False, "Quelle nicht erlaubt – nutze Stock (Mixkit/Pexels) oder setze JAM_REMIX_I_HAVE_RIGHTS nur bei eigenen Rechten."
    try:
        import requests

        dest.parent.mkdir(parents=True, exist_ok=True)
        headers = {
            "User-Agent": (
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
            ),
            "Accept": "*/*",
        }
        if "mixkit.co" in url.lower():
            headers["Referer"] = "https://mixkit.co/"
        if "pexels.com" in url.lower():
            headers["Referer"] = "https://www.pexels.com/"
        with requests.get(url, stream=True, timeout=120, headers=headers) as r:
            r.raise_for_status()
            size = 0
            with dest.open("wb") as f:
                for chunk in r.iter_content(65536):
                    if not chunk:
                        continue
                    size += len(chunk)
                    if size > MAX_BYTES:
                        return False, "Datei zu groß (>80 MB)."
                    f.write(chunk)
        return True, str(dest)
    except Exception as e:
        return False, str(e)[:120]


def download_pexels_video(query: str, dest: Path) -> tuple[bool, str]:
    key = os.environ.get("PEXELS_API_KEY", "").strip()
    if not key:
        return False, "Kein PEXELS_API_KEY – Mixkit-Fallback oder Key in Umgebung setzen."
    try:
        import requests

        r = requests.get(
            "https://api.pexels.com/videos/search",
            headers={"Authorization": key},
            params={"query": query, "per_page": 8, "orientation": "portrait"},
            timeout=25,
        )
        r.raise_for_status()
        videos = r.json().get("videos") or []
        if not videos:
            return False, "Kein Pexels-Video gefunden."
        vid = random.choice(videos[:5])
        files = vid.get("video_files") or []
        files.sort(key=lambda x: int(x.get("width", 0) or 0), reverse=True)
        link = None
        for f in files:
            if f.get("link") and _host_allowed(f["link"]):
                link = f["link"]
                break
        if not link:
            return False, "Kein Download-Link."
        return download_video_url(link, dest)
    except Exception as e:
        return False, str(e)[:120]


def download_pexels_curated(dest: Path, *, avoid_urls: set[str] | None = None) -> tuple[bool, str, str]:
    """Hochwertige Pexels-Clips (9:16 / cinematic) ohne API-Key."""
    avoid = avoid_urls or set()
    pool = [c for c in CURATED_PEXELS_CLIPS if c[1] not in avoid]
    random.shuffle(pool)
    for label, url in pool:
        ok, msg = download_video_url(url, dest)
        if ok:
            return True, f"Pexels ({label})", label
    return False, "Kein kuratierter Pexels-Clip erreichbar.", ""


def download_stock_clip(
    query: str = "social media smartphone",
    *,
    prefer_unusual: bool = True,
    hd: bool = False,
) -> tuple[Path | None, str]:
    """Stock-Clip laden (Pexels kuratiert → API → Mixkit)."""
    REMIX_SRC.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    dest = REMIX_SRC / f"stock-{stamp}.mp4"

    if hd or prefer_unusual:
        ok, msg, _label = download_pexels_curated(dest)
        if ok:
            return dest, msg + (" · HD" if hd else "")

    ok, msg = download_pexels_video(query, dest)
    if ok:
        return dest, "Pexels API"

    mixkit_urls = list(MIXKIT_SAMPLES)
    random.shuffle(mixkit_urls)
    for url in mixkit_urls:
        ok, msg = download_video_url(url, dest)
        if ok:
            return dest, "Mixkit"
    for url in FALLBACK_CLIPS:
        ok, msg = download_video_url(url, dest)
        if ok:
            return dest, "Fallback-Clip"
    return None, msg


def remix_video(
    src: Path,
    dst: Path,
    *,
    hook: str = "@socialmedia.mentor",
    max_sec: int = 55,
) -> tuple[bool, str]:
    """Vertikal, kürzen, Text-Overlay – erkennbar verändert."""
    if not src.is_file():
        return False, "Quelldatei fehlt."
    ffmpeg = _find_ffmpeg()
    dst.parent.mkdir(parents=True, exist_ok=True)
    hook_safe = re.sub(r"[^a-zA-Z0-9 @#._-]", "", hook)[:40] or "socialmedia.mentor"

    if not ffmpeg:
        shutil.copy2(src, dst)
        (dst.with_suffix(".txt")).write_text(
            f"ffmpeg fehlt – Original kopiert. Overlay manuell: {hook_safe}\n",
            encoding="utf-8",
        )
        return True, "Kopie ohne ffmpeg (Text in CapCut/TikTok ergänzen)."

    # TikTok 9:16, leichter Zoom, Text unten, etwas schneller
    vf = (
        f"scale=1080:1920:force_original_aspect_ratio=increase,"
        f"crop=1080:1920,"
        f"drawtext=text='{hook_safe}':fontsize=42:fontcolor=white:"
        f"borderw=3:bordercolor=black:x=(w-text_w)/2:y=h-120,"
        f"eq=brightness=0.03:saturation=1.08"
    )
    cmd = [
        ffmpeg,
        "-y",
        "-i",
        str(src),
        "-t",
        str(max_sec),
        "-vf",
        vf,
        "-an",
        "-c:v",
        "libx264",
        "-preset",
        "fast",
        "-crf",
        "23",
        str(dst),
    ]
    try:
        subprocess.run(cmd, capture_output=True, timeout=180, check=True)
        if dst.is_file() and dst.stat().st_size > 1000:
            return True, "Remix mit ffmpeg (9:16, Text, Farbe)."
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired, OSError) as e:
        shutil.copy2(src, dst)
        return True, f"ffmpeg Fallback Kopie: {e}"[:80]
    return False, "Remix fehlgeschlagen."


def open_stock_search(query: str = "social media marketing") -> None:
    q = quote_plus(query)
    for _name, tpl in STOCK_SEARCH:
        try:
            webbrowser.open(tpl.format(q=q), new=2)
        except OSError:
            pass


def pick_fresh_query() -> str:
    return random.choice(FRESH_STOCK_QUERIES)


def run_remix_and_upload(
    brain: JamBrain,
    *,
    url: str | None = None,
    query: str = "",
    slot: str = "mittag",
) -> str:
    if not query:
        query = pick_fresh_query()
    """Download → Remix → clips-ready → TikTok-Upload vorbereiten."""
    owner = owner_name(brain.state)
    REMIX_OUT.mkdir(parents=True, exist_ok=True)
    CLIPS_READY.mkdir(parents=True, exist_ok=True)

    src_path: Path | None = None
    source_note = ""

    if url and url.strip():
        src_path = REMIX_SRC / f"url-{int(time.time())}.mp4"
        ok, msg = download_video_url(url.strip(), src_path)
        if not ok:
            return f"{owner}, Download klappt nicht: {msg}"[:400]
        source_note = "URL"
    else:
        src_path, source_note = download_stock_clip(query)
        if not src_path:
            open_stock_search(query)
            return (
                f"{owner}, Stock-Download fehlgeschlagen ({source_note}). "
                f"Ich habe **Mixkit/Pexels** im Browser geöffnet – MP4-Link schicken oder "
                f"PEXELS_API_KEY setzen. Oder: „Jam, Remix diese URL: https://…mp4“"
            )[:520]

    out_name = f"{slot}-remix-{datetime.now().strftime('%H%M')}.mp4"
    out_path = REMIX_OUT / out_name
    hook = "Social Media Mentor"
    try:
        from jam.tiktok_post import _build_post

        post = _build_post(brain.state, slot)
        hook = (post.get("hook") or hook)[:50]
    except Exception:
        pass

    ok, remix_msg = remix_video(src_path, out_path, hook=hook)
    if not ok:
        return f"{owner}, Remix fehlgeschlagen: {remix_msg}"[:400]

    ready = CLIPS_READY / f"{slot}.mp4"
    shutil.copy2(out_path, ready)

    legal = REMIX_SRC / "RECHTLICH.txt"
    if not legal.is_file():
        legal.write_text(
            "Nur Stock (Mixkit/Pexels/Pixabay) oder eigene Rechte.\n"
            "Fremde TikToks/Reels nicht 1:1 reuploaden ohne Erlaubnis.\n"
            "Jam verändert Clips (Schnitt, 9:16, Text, Farbe) – trotzdem Rechte beachten.\n",
            encoding="utf-8",
        )

    upload_msg = ""
    try:
        from jam.tiktok_post import run_post_round

        upload_msg = run_post_round(brain, slot=slot, user_initiated=True, open_upload=True)
    except Exception as e:
        upload_msg = f"Upload: {e}"

    brain.state.remember(f"TikTok Remix {source_note} → {slot}")
    brain.state.last_action = "tiktok_remix"
    brain.add_xp(12)
    brain.save()

    return (
        f"{owner}, erledigt! **{source_note}** → Remix (**{remix_msg}**) → "
        f"`{ready.name}` bereit. {upload_msg}"
    )[:720]


def hears_remix(text: str) -> bool:
    h = text.lower().replace("jam", " ")
    if any(w in h for w in ("remix", "veränder", "veraender", "bearbeit", "umändern")):
        if "tiktok" in h or "video" in h or "clip" in h:
            return True
    if any(w in h for w in ("runterlad", "download", "lad")) and any(
        w in h for w in ("hochlad", "upload", "tiktok", "video", "wieder")
    ):
        return True
    if "von woanders" in h or "woanders" in h:
        if any(w in h for w in ("lad", "hol", "video", "clip", "tiktok")):
            return True
    return False


def parse_url_from_speech(text: str) -> str | None:
    m = re.search(r"https?://[^\s]+", text, re.I)
    if m:
        return m.group(0).rstrip(".,)")
    return None


def handle_speech(brain: JamBrain, heard: str) -> str | None:
    if not hears_remix(heard):
        return None
    url = parse_url_from_speech(heard)
    q = "social media mentor"
    if "gaming" in heard.lower():
        q = "gaming smartphone"
    return run_remix_and_upload(brain, url=url, query=q)
