import os
import json
import time
from typing import List, Dict, Any, Optional
import uuid
import requests
import streamlit as st
from pydantic import BaseModel, Field
from dotenv import load_dotenv

# Optional: YouTube transcript
from youtube_transcript_api import YouTubeTranscriptApi, TranscriptsDisabled, NoTranscriptFound

load_dotenv()

# -----------------------------
# ENV
# -----------------------------
CLOVA_API_KEY = os.getenv("CLOVA_API_KEY", "")
CLOVA_CHAT_URL = os.getenv("CLOVA_CHAT_URL", "")
CLOVA_MODEL = os.getenv("CLOVA_MODEL", "HCX-007")

YOUTUBE_API_KEY = os.getenv("YOUTUBE_API_KEY", "")
NOTION_API_KEY = os.getenv("NOTION_API_KEY", "")
NOTION_PAGE_ID = os.getenv("NOTION_PAGE_ID", "")  # ✅ 기존 페이지 ID 사용
# -----------------------------
# Pydantic Schemas for Tools
# -----------------------------
class NotionPagePayload(BaseModel):
    title: str = Field(..., description="Notion 페이지 제목 (보통 영상 제목 또는 요약 제목)")
    url: str = Field(..., description="원본 YouTube 영상 URL")
    keywords: List[str] = Field(default_factory=list, description="핵심 키워드 3~7개")
    summary: str = Field(..., description="한글 요약 (핵심 bullet 포함)")
    takeaways: List[str] = Field(default_factory=list, description="핵심 인사이트 bullet (3~5개)")

# -----------------------------
# YouTube Helpers
# -----------------------------
def yt_search(query: str, max_results: int = 5) -> List[Dict[str, Any]]:
    base = "https://www.googleapis.com/youtube/v3/search"
    params = {
        "part": "snippet",
        "q": query,
        "type": "video",
        "maxResults": max_results,
        "key": YOUTUBE_API_KEY,
    }
    r = requests.get(base, params=params, timeout=20)
    r.raise_for_status()
    items = r.json().get("items", [])
    videos = []
    for it in items:
        vid = it["id"]["videoId"]
        sn = it["snippet"]
        videos.append({
            "video_id": vid,
            "title": sn.get("title", ""),
            "description": sn.get("description", ""),
            "channelTitle": sn.get("channelTitle", ""),
            "publishedAt": sn.get("publishedAt", ""),
            "url": f"https://www.youtube.com/watch?v={vid}",
        })
    return videos

def yt_try_transcript(video_id: str) -> Optional[str]:
    """자막 허용 영상이면 자막을 받아서 텍스트로 합침. 실패하면 None"""
    try:
        transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=["ko", "en"])
        text = " ".join([seg["text"] for seg in transcript if "text" in seg])
        return text.strip()
    except (TranscriptsDisabled, NoTranscriptFound):
        return None
    except Exception:
        return None

# -----------------------------
# Notion Helpers
# -----------------------------
# -----------------------------
# Notion Helpers (append to existing page)
# -----------------------------
def _notion_headers():
    return {
        "Authorization": f"Bearer {NOTION_API_KEY}",
        "Content-Type": "application/json",
        "Notion-Version": "2025-09-03",
    }

def notion_update_page_title(page_id: str, new_title: str) -> Dict[str, Any]:
    """
    (옵션) 독립 페이지의 타이틀을 바꿉니다.
    DB 아이템이 아니라 '일반 페이지'면 properties.title 로 제목을 바꿀 수 있습니다.
    """
    url = f"https://api.notion.com/v1/pages/{page_id}"
    body = {
        "properties": {
            "title": {
                "title": [{"type": "text", "text": {"content": new_title[:200]}}]
            }
        }
    }
    resp = requests.patch(url, headers=_notion_headers(), data=json.dumps(body), timeout=30)
    resp.raise_for_status()
    return resp.json()

def notion_append_blocks(page_id: str, payload: NotionPagePayload) -> Dict[str, Any]:
    """
    기존 페이지에 요약/키워드/테이크어웨이/북마크를 블록으로 Append.
    """
    url = f"https://api.notion.com/v1/blocks/{page_id}/children"

    # 본문 블록 구성
    blocks = []

    # 섹션 헤더: 영상 제목
    blocks.append({
        "object": "block",
        "type": "heading_1",
        "heading_1": {"rich_text": [{"type": "text", "text": {"content": payload.title[:200]}}]}
    })

    # 키워드(있다면)
    if payload.keywords:
        blocks.append({
            "object": "block",
            "type": "callout",
            "callout": {
                "rich_text": [{
                    "type": "text",
                    "text": {"content": "키워드: " + ", ".join(payload.keywords[:10])}
                }],
                "icon": {"type": "emoji", "emoji": "🔎"}
            }
        })

    # 요약
    blocks.extend([
        {
            "object": "block",
            "type": "heading_2",
            "heading_2": {"rich_text": [{"type": "text", "text": {"content": "요약"}}]}
        },
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {"rich_text": [{"type": "text", "text": {"content": payload.summary[:2000]}}]}
        },
    ])

    # Takeaways
    blocks.append({
        "object": "block",
        "type": "heading_2",
        "heading_2": {"rich_text": [{"type": "text", "text": {"content": "Takeaways"}}]}
    })
    for t in payload.takeaways[:10]:
        blocks.append({
            "object": "block",
            "type": "bulleted_list_item",
            "bulleted_list_item": {"rich_text": [{"type": "text", "text": {"content": t}}]}
        })

    # 원본 링크 북마크
    if payload.url:
        blocks.append({
            "object": "block",
            "type": "bookmark",
            "bookmark": {"url": payload.url}
        })

    # 구분선
    blocks.append({"object": "block", "type": "divider", "divider": {}})

    body = {"children": blocks}
    resp = requests.patch(url, headers=_notion_headers(), data=json.dumps(body), timeout=30)
    resp.raise_for_status()
    return resp.json()

def notion_get_page_url(page_id: str) -> Optional[str]:
    """
    페이지 URL 가져오기 (공유 링크 아님, Notion 내부용 URL)
    """
    url = f"https://api.notion.com/v1/pages/{page_id}"
    resp = requests.get(url, headers=_notion_headers(), timeout=20)
    if resp.status_code == 200:
        return resp.json().get("url")
    return None

# -----------------------------
def clova_chat_with_tools(system_prompt: str, user_prompt: str, tool_schema: Dict[str, Any]) -> Dict[str, Any]:
    """
    v3 Chat Completions(Function Calling) 호출.
    - 경로: {BASE}/v3/chat-completions/{modelName}
    - 헤더: Authorization: Bearer <KEY>
    """

    url = f"{CLOVA_CHAT_URL.rstrip('/')}/v3/chat-completions/{CLOVA_MODEL}"

    headers = {
        "Authorization": f"Bearer {CLOVA_API_KEY}",
        "X-NCP-CLOVASTUDIO-REQUEST-ID": str(uuid.uuid4()),
        "Content-Type": "application/json",
    }

    body = {
        "messages": [
            {"role": "user", "content": system_prompt + "\n\n" + user_prompt}
        ],
        "tools": [tool_schema],   # [{"type":"function","function":{...}}]
        "toolChoice": "auto",     # camelCase

    }

    resp = requests.post(url, headers=headers, json=body, timeout=60)
    resp.raise_for_status()
    return resp.json()

def parse_tool_call(resp_json: Dict[str, Any]) -> Optional[Dict[str, Any]]:

    def _ensure_dict(x: Any) -> Dict[str, Any]:
        if isinstance(x, dict):
            return x
        if isinstance(x, str):
            try:
                return json.loads(x)
            except Exception:
                return {}
        return {}

    msg = (resp_json.get("result") or {}).get("message") or {}
    tool_calls = msg.get("toolCalls") or []
    if isinstance(tool_calls, list) and tool_calls:
        fn = (tool_calls[0] or {}).get("function") or {}
        return {"name": fn.get("name"), "arguments": _ensure_dict(fn.get("arguments"))}

    try:
        tc = resp_json["choices"][0]["message"].get("tool_calls", [])
        if tc:
            fn = (tc[0] or {}).get("function", {})
            return {"name": fn.get("name"), "arguments": _ensure_dict(fn.get("arguments"))}
    except Exception:
        pass
    try:
        fc = resp_json["choices"][0]["message"].get("function_call")
        if fc:
            return {"name": fc.get("name"), "arguments": _ensure_dict(fc.get("arguments"))}
    except Exception:
        pass

    return None

# 요약
def build_system_prompt() -> str:
    return (
        """너는 사용자 요청에 대해 절대로 자연어 답변을 하지 않는다.
        반드시 write_notion_blocks 호출한다.
        """
    )
def build_user_prompt(video: Dict[str, Any], text_source: str) -> str:

    return (
        f"[영상 메타정보]\n"
        f"- 제목: {video['title']}\n"
        f"- 채널: {video.get('channelTitle','')}\n"
        f"- URL: {video['url']}\n"
        f"- 게시일: {video.get('publishedAt','')}\n\n"
        f"[요약 대상 텍스트]\n{text_source[:8000]}\n\n"
        "요약은 한국어로 작성하고, 핵심 bullet로 takeaways 3~5개를 제공한 뒤, "
        "아래 함수 명세에 맞춰 write_notion_blocks를 호출해."
    )

def get_tool_schema() -> Dict[str, Any]:
    return {
        "type": "function",
        "function": {
            "name": "write_notion_blocks",
            "description": "기존 Notion 페이지에 영상 요약 블록을 추가",
            "parameters": {
                "type": "object",
                "properties": {
                    "title": {"type": "string", "description": "영상 제목"},
                    "url": {"type": "string", "description": "원본 영상 URL"},
                    "keywords": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "핵심 키워드 3~7개"
                    },
                    "summary": {"type": "string", "description": "영상 요약(한글)"},
                    "takeaways": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "핵심 인사이트 bullet 3~5개"
                    },
                },
                "required": ["title", "summary"]
            }
        }
    }


def to_payload(args: Dict[str, Any]) -> NotionPagePayload:
    return NotionPagePayload(
        title=args.get("title") or "Untitled",
        url=args.get("url") or "",
        keywords=args.get("keywords") or [],
        summary=args.get("summary") or "",
        takeaways=args.get("takeaways") or [],
    )

st.set_page_config(page_title="YouTube ▶CLOVA ▶ Notion", page_icon="🧩", layout="centered")
st.title("YouTube 검색 → CLOVA 요약 → Notion")

with st.sidebar:
    st.subheader("환경 변수 체크")
    st.write(f"Clova URL: `{CLOVA_CHAT_URL or '미설정'}`")
    st.write(f"Model: `{CLOVA_MODEL}`")
    st.write("YouTube / Notion 키가 설정되어 있어야 합니다.")
    st.write(f"Notion Page ID: `{NOTION_PAGE_ID or '미설정'}`")

query = st.text_input("YouTube 검색어를 입력하세요", value="AI education trend 2025")
max_results = st.slider("검색 개수", min_value=1, max_value=5, value=1)
run = st.button("검색 → 요약 → Notion에 추가")

if run:
    if not (CLOVA_API_KEY and CLOVA_CHAT_URL and YOUTUBE_API_KEY and NOTION_API_KEY and NOTION_PAGE_ID):
        st.error("⚠.env 환경변수를 모두 설정해주세요. (특히 NOTION_PAGE_ID)")
        st.stop()

    with st.spinner("YouTube 검색 중..."):
        videos = yt_search(query, max_results=max_results)

    if not videos:
        st.warning("검색 결과가 없습니다.")
        st.stop()

    tool_schema = get_tool_schema()
    system_prompt = build_system_prompt()

    results = []
    for i, v in enumerate(videos, start=1):
        st.markdown(f"### {i}. [{v['title']}]({v['url']})  \n채널: {v['channelTitle']} / 게시일: {v['publishedAt']}")
        with st.spinner("자막/설명 수집 및 요약 생성 중..."):
            text = yt_try_transcript(v["video_id"]) or v["description"] or v["title"]
            uprompt = build_user_prompt(v, text_source=text)
            try:
                resp = clova_chat_with_tools(system_prompt=system_prompt, user_prompt=uprompt, tool_schema=tool_schema)
            except requests.HTTPError as e:
                st.error(f"CLOVA 호출 실패: {e.response.text}")
                continue

            tool_call = parse_tool_call(resp)
            if not tool_call or tool_call.get("name") != "write_notion_blocks":  #함수명 체크
                st.warning("모델이 함수 호출을 생성하지 않았습니다. (영상이 짧아서 요약이 불가합니다.)")
                st.json(resp)
                continue

            args = tool_call["arguments"]
            payload = to_payload(args)

        with st.spinner("Notion 페이지에 블록 추가 중..."):
            try:
                # (옵션) 페이지 제목을 payload.title로 동기화하고 싶다면 주석 해제
                # notion_update_page_title(NOTION_PAGE_ID, payload.title)

                appended = notion_append_blocks(NOTION_PAGE_ID, payload)
                page_url = notion_get_page_url(NOTION_PAGE_ID) or ""
                st.success("Notion 페이지에 내용이 추가되었습니다!")
                if page_url:
                    st.write(f"[Notion에서 해당 페이지 열기]({page_url})")
                results.append({"video": v, "pageUrl": page_url, "payload": payload.dict()})
            except requests.HTTPError as e:
                st.error(f"Notion Append 실패: {e.response.text}")

        time.sleep(0.6)  # API 빈도 조절

