"""Kling AI video generation — Native KlingAI API (api.klingai.com).

JWT auth: HS256, payload {iss: ACCESS_KEY, exp: now+1800, nbf: now-5}.
POST /v1/videos/text2video → GET /v1/videos/text2video/{task_id} → task_result.videos[0].url 다운로드.
"""
from __future__ import annotations

import logging
import os
import time
from typing import Optional

import jwt as pyjwt
import requests

from nas_client import webhook_update_task

logger = logging.getLogger(__name__)

KLING_BASE_URL = "https://api.klingai.com"
VIDEO_MEDIA_ROOT = os.getenv("VIDEO_MEDIA_ROOT", "/mnt/nas/webpage/data/video")
VIDEO_MEDIA_URL_PREFIX = os.getenv("VIDEO_MEDIA_URL_PREFIX", "/media/video")

POLL_INTERVAL = 10
POLL_MAX_ATTEMPTS = 60   # 최대 ~10분

DEFAULT_MODEL = "kling-v1-6"

JWT_EXP_SECONDS = 1800   # 30분
JWT_NBF_OFFSET = -5      # 5초 뒤로


def _generate_jwt() -> Optional[str]:
    access_key = os.getenv("KLING_ACCESS_KEY", "")
    secret_key = os.getenv("KLING_SECRET_KEY", "")
    if not access_key or not secret_key:
        return None
    now = int(time.time())
    headers = {"alg": "HS256", "typ": "JWT"}
    payload = {"iss": access_key, "exp": now + JWT_EXP_SECONDS, "nbf": now + JWT_NBF_OFFSET}
    return pyjwt.encode(payload, secret_key, algorithm="HS256", headers=headers)


def _headers() -> dict:
    token = _generate_jwt()
    return {
        "Authorization": f"Bearer {token}" if token else "",
        "Content-Type": "application/json",
    }


def run_kling_generation(task_id: str, params: dict) -> None:
    """Kling으로 영상 생성 → mp4 → NAS SMB → webhook."""
    try:
        if not os.getenv("KLING_ACCESS_KEY") or not os.getenv("KLING_SECRET_KEY"):
            webhook_update_task(task_id, "failed", 0, "",
                                error="KLING_ACCESS_KEY 또는 KLING_SECRET_KEY 미설정")
            return

        webhook_update_task(task_id, "processing", 5, "Kling API 호출 중...")

        # image_url 있으면 image2video, 없으면 text2video
        is_image2video = bool(params.get("image_url"))
        endpoint_path = "/v1/videos/image2video" if is_image2video else "/v1/videos/text2video"

        body = {
            "model_name": params.get("model") or DEFAULT_MODEL,
            "prompt": params["prompt"][:2500],
            "duration": str(params.get("duration", 5)),
            "aspect_ratio": params.get("aspect_ratio", "16:9"),
            "mode": params.get("mode", "std"),
        }
        if params.get("negative_prompt"):
            body["negative_prompt"] = params["negative_prompt"][:2500]
        if params.get("cfg_scale") is not None:
            body["cfg_scale"] = float(params["cfg_scale"])
        if is_image2video:
            body["image"] = params["image_url"]

        resp = requests.post(f"{KLING_BASE_URL}{endpoint_path}",
                             headers=_headers(), json=body, timeout=30)
        if resp.status_code != 200:
            webhook_update_task(task_id, "failed", 0, "",
                                error=f"Kling API 오류: {resp.status_code} {resp.text[:300]}")
            return

        body_json = resp.json()
        if body_json.get("code") != 0:
            webhook_update_task(task_id, "failed", 0, "",
                                error=f"Kling API 거부: {body_json.get('message', '?')}")
            return

        kling_task_id = (body_json.get("data") or {}).get("task_id", "")
        if not kling_task_id:
            webhook_update_task(task_id, "failed", 0, "", error="Kling 응답에 task_id 없음")
            return

        webhook_update_task(task_id, "processing", 15, "Kling 작업 등록됨")

        # 폴링 — GET /v1/videos/{text2video|image2video}/{task_id}
        video_url = None
        for attempt in range(POLL_MAX_ATTEMPTS):
            time.sleep(POLL_INTERVAL)
            fetch = requests.get(f"{KLING_BASE_URL}{endpoint_path}/{kling_task_id}",
                                 headers=_headers(), timeout=30)
            if fetch.status_code != 200:
                continue
            fd = fetch.json()
            if fd.get("code") != 0:
                continue
            data = fd.get("data") or {}
            status = data.get("task_status", "")
            scaled = min(15 + int((attempt / POLL_MAX_ATTEMPTS) * 65), 79)
            webhook_update_task(task_id, "processing", scaled, f"Kling 생성 중... ({status})")

            if status == "succeed":
                videos = ((data.get("task_result") or {}).get("videos") or [])
                if videos:
                    video_url = videos[0].get("url", "")
                break
            elif status == "failed":
                err = data.get("task_status_msg") or "Kling 작업 실패"
                webhook_update_task(task_id, "failed", 0, "", error=err)
                return
            # submitted/processing → 계속 폴링
        else:
            webhook_update_task(task_id, "failed", 0, "", error="Kling 폴링 timeout (10분)")
            return

        if not video_url:
            webhook_update_task(task_id, "failed", 0, "", error="Kling 완료했으나 video url 없음")
            return

        webhook_update_task(task_id, "processing", 85, "Kling 결과 다운로드 중...")
        filename = f"{task_id}.mp4"
        os.makedirs(VIDEO_MEDIA_ROOT, exist_ok=True)
        file_path = os.path.join(VIDEO_MEDIA_ROOT, filename)

        # Kling 결과 url은 일반적으로 인증 불필요 (signed URL)
        dl = requests.get(video_url, stream=True, timeout=300)
        dl.raise_for_status()
        with open(file_path, "wb") as f:
            for chunk in dl.iter_content(chunk_size=8192):
                f.write(chunk)

        local_url = f"{VIDEO_MEDIA_URL_PREFIX}/{filename}"
        webhook_update_task(task_id, "succeeded", 100, "Kling 생성 완료", video_url=local_url)

    except requests.Timeout:
        webhook_update_task(task_id, "failed", 0, "", error="Kling API 타임아웃")
    except Exception as e:
        logger.exception("Kling generation error task=%s", task_id)
        webhook_update_task(task_id, "failed", 0, "", error=str(e))
