import json
import os
import tempfile
from datetime import datetime
from pathlib import Path


STORE_FILE = "videos.jsonl"


def now_iso():
    return datetime.now().isoformat(timespec="seconds")


def ensure_store(path=STORE_FILE):
    Path(path).touch(exist_ok=True)


def load_videos(path=STORE_FILE):
    """
    Charge toutes les vidéos depuis le fichier JSONL.
    Retourne un dictionnaire indexé par URL.
    """
    ensure_store(path)
    videos = {}

    with open(path, "r", encoding="utf-8") as f:
        for line_number, line in enumerate(f, start=1):
            line = line.strip()
            if not line:
                continue

            try:
                video = json.loads(line)
                url = video.get("url")
                if url:
                    videos[url] = video
            except json.JSONDecodeError:
                print(f"⚠ Ligne JSON invalide ignorée : {line_number}")

    return videos

def json_serializer(obj):
    """
    Convertit les objets non sérialisables en JSON.
    """
    from datetime import datetime, date

    if isinstance(obj, (datetime, date)):
        return obj.isoformat()

    return str(obj)  # fallback sécurisé

def save_all_videos(videos_by_url, path=STORE_FILE):
    directory = Path(path).parent
    directory.mkdir(parents=True, exist_ok=True)

    fd, tmp_path = tempfile.mkstemp(
        dir=directory,
        prefix=".videos_",
        suffix=".jsonl",
        text=True
    )

    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            for video in videos_by_url.values():
                f.write(json.dumps(
                    video,
                    ensure_ascii=False,
                    default=json_serializer
                ) + "\n")

        os.replace(tmp_path, path)

    except Exception:
        if os.path.exists(tmp_path):
            os.remove(tmp_path)
        raise

def normalize_video(video):
    from datetime import datetime

    for key in ["published_at", "discovered_at", "created_at", "updated_at"]:
        if isinstance(video.get(key), datetime):
            video[key] = video[key].isoformat()

    return video

def add_or_update_video(video, path=STORE_FILE):
    """
    Ajoute ou met à jour une vidéo selon son URL.
    """
    video = normalize_video(video)

    if "url" not in video or not video["url"]:
        raise ValueError("La vidéo doit contenir une clé 'url'.")

    videos = load_videos(path)
    url = video["url"]

    timestamp = now_iso()

    if url in videos:
        old = videos[url]
        old.update({k: v for k, v in video.items() if v is not None})
        old["updated_at"] = timestamp
        videos[url] = old
    else:
        video.setdefault("status", "new")
        video.setdefault("created_at", timestamp)
        video.setdefault("updated_at", timestamp)
        video.setdefault("discovered_at", timestamp)
        videos[url] = video

    save_all_videos(videos, path)


def add_or_update_many(video_list, path=STORE_FILE):
    """
    Ajoute ou met à jour plusieurs vidéos en une seule réécriture du fichier.
    Beaucoup plus efficace que add_or_update_video appelée 10 000 fois.
    """
    videos = load_videos(path)
    timestamp = now_iso()

    for video in video_list:
        video = normalize_video(video)
        if "url" not in video or not video["url"]:
            continue

        url = video["url"]

        if url in videos:
            old = videos[url]
            old.update({k: v for k, v in video.items() if v is not None})
            old["updated_at"] = timestamp
            videos[url] = old
        else:
            video.setdefault("status", "new")
            video.setdefault("created_at", timestamp)
            video.setdefault("updated_at", timestamp)
            video.setdefault("discovered_at", timestamp)
            videos[url] = video

    save_all_videos(videos, path)


def get_all_videos(path=STORE_FILE):
    return list(load_videos(path).values())


def get_by_status(status, path=STORE_FILE, limit=None):
    videos = load_videos(path).values()

    result = [
        v for v in videos
        if v.get("status") == status
    ]

    result.sort(key=lambda v: v.get("published_at") or "", reverse=True)

    if limit:
        result = result[:limit]

    return result


def get_videos_to_transcribe(path=STORE_FILE, limit=20):
    videos = load_videos(path).values()

    result = [
        v for v in videos
        if v.get("status") in ("new", "metadata_ok")
        and not v.get("transcript")
    ]

    result.sort(key=lambda v: v.get("published_at") or "", reverse=True)

    return result[:limit]


def update_video_by_url(url, updates, path=STORE_FILE):
    videos = load_videos(path)

    if url not in videos:
        raise KeyError(f"URL absente du fichier : {url}")

    videos[url].update(updates)
    videos[url]["updated_at"] = now_iso()

    save_all_videos(videos, path)


def save_transcript(url, transcript, path=STORE_FILE):
    update_video_by_url(url, {
        "transcript": transcript,
        "status": "transcribed"
    }, path)


def save_summary(url, summary, path=STORE_FILE):
    update_video_by_url(url, {
        "summary": summary,
        "status": "summarized"
    }, path)


def mark_error(url, error_message, path=STORE_FILE):
    update_video_by_url(url, {
        "status": "error",
        "error_message": error_message
    }, path)


def mark_ignored(url, reason=None, path=STORE_FILE):
    updates = {"status": "ignored"}
    if reason:
        updates["ignored_reason"] = reason

    update_video_by_url(url, updates, path)


def search_videos(keyword, path=STORE_FILE):
    keyword = keyword.lower()
    videos = load_videos(path).values()

    return [
        v for v in videos
        if keyword in str(v.get("title", "")).lower()
        or keyword in str(v.get("description", "")).lower()
        or keyword in str(v.get("channel_name", "")).lower()
        or keyword in str(v.get("summary", "")).lower()
    ]
