import os
import time
import asyncio
from typing import Dict, List, Tuple
import uuid
import shutil
from playwright.async_api import async_playwright
from openai import AsyncOpenAI
import boto3

# FastAPI及び関連ライブラリ
from fastapi import APIRouter, HTTPException, File, UploadFile

# ⚙️ ローカルモジュールのインポート
from config import logger, openai_api_key
from schemas import SpeechText, ChatRequest
import db_module

# ===================================================
# 🔧 初期設定
# ===================================================
client = AsyncOpenAI(api_key=openai_api_key)
s3_client = boto3.client('s3')
bucket_name = 'shanri-ai-chatbot-for-text-to-speech'

router = APIRouter()

# ===================================================
# ⚙️ サービスロジック
# ===================================================

# --- スクレイピング関連のサービス ---
class KeywordGenerator:
    # // 書籍のタイトルからキーワードを生成します
    async def generate_keywords(self, title: str) -> List[str]:
        prompt = f"""
        以下の書籍のタイトルから、検索キーワードを生成してください。
        以下の条件を満たすようにしてください：
        - 本の内容を推測できるキーワードを抽出
        - 一般的な検索キーワードとして使えそうなものを選択
        - 単語や短いフレーズを抽出
        - 重複を避ける
        - 数字や括弧などの記号は含めない

        タイトル: {title}

        キーワードはカンマ区切りで出力してください。
        """
        try:
            response = await client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=[
                    {"role": "system", "content": "あなたは書籍の内容を分析し、適切な検索キーワードを生成する専門家です。"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.7, max_tokens=100
            )
            keywords_text = response.choices[0].message.content
            return [kw.strip() for kw in keywords_text.split(",") if kw.strip()]
        except Exception as e:
            logger.error(f"Error generating keywords for '{title}': {e}")
            return []

class RakutenScraper:
    # // 楽天ブックスのランキングをスクレイピングします
    def __init__(self):
        self.base_url = "https://books.rakuten.co.jp"
        self.keyword_generator = KeywordGenerator()

    async def scrape(self) -> List[Dict]:
        logger.info("Rakuten Books scraping process started.")
        try:
            await db_module.clear_ranking_data()

            async with async_playwright() as p:
                browser = await p.chromium.launch()
                page = await browser.new_page()
                try:
                    page.set_default_timeout(60000)
                    url = f"{self.base_url}/ranking/hourly/001/"
                    await page.goto(url, wait_until="networkidle")

                    selector = ".item"
                    items = await page.query_selector_all(selector)

                    if not items: return []

                    scraped_results = []
                    for item in items:
                        title_element = await item.query_selector(".title")
                        if not title_element: continue
                        title = (await title_element.text_content()).strip()

                        author_element = await item.query_selector(".author")
                        author = (await author_element.text_content()).strip() if author_element else "不明"

                        scraped_results.append({"title": title, "author": author, "source": "rakuten"})

                    await db_module.save_book_rankings(scraped_results)

                    for book in scraped_results:
                        keywords = await self.keyword_generator.generate_keywords(book["title"])
                        book["keywords"] = keywords
                        if keywords:
                            await db_module.save_keywords_list(keywords)
                            await db_module.link_keywords_to_book(book["title"], keywords)

                    logger.info(f"Successfully processed {len(scraped_results)} items.")
                    return scraped_results
                finally:
                    await browser.close()
        except Exception as e:
            logger.error(f"Scraping failed: {e}")
            raise e

# --- チャット関連のサービス ---

SYSTEM_PROMPT_CHAT = (
    "あなたは「どんな本を読もうか思いつかない方」や「少し読みたい本が決まっている方」を支える相談サポーターです。"
    "相談者が自由にお気持ちや関心事を話せるように、寄り添いながら丁寧に傾聴してください。"
    "あなたの役割は「安心できる聞き役」であり、本を紹介・創作したり著者を答えたりしないでください。"
    "--------------------------------"
    "【会話スタイル】"
    "・丁寧に傾聴し、安心感があり楽しい雰囲気をつくる"
    "・ご相談者のお気持ちや関心事を理解するよう努める"
    "・相談者の言葉に共感の言葉を添える"
    "・一度にひとつだけ、やさしく質問する"
    "・本や著者の情報提案はせず、あくまで寄り添う"
    ""
    "【会話の手順】"
    "1. 相談者の回答を受けて、まずは共感の言葉で受け止める："
    "『○○にご興味があるのですね。』"
    ""
    "2. その後、相談者の興味・気分・経験・思い出などを軽く質問し、"
    "共感のリアクションや感嘆なども添えながら、会話を楽しく広げる："
    "（例：『最近気になっていることはありますか？』『以前からお好きだったことはありますか？』）"
    ""
    "3. 寄り添いの言葉を交えながら優しく質問し、対話を3〜5往復程度続けてください。"
    ""
    "4. 対話を3〜5往復、会話の最後に次のように案内する："
    "   『お話しくださってありがとうございます。お話から面白そうなキーワードをいくつか整理しました！気になるものを押していただければ、本を探すことができますよ。』"
)

questions = [
    "こんにちは。お話ししながら、あなたにぴったりの本を一緒に探しましょう。「今日はこんな本が読みたいな」と思ったことを、マイクボタンを一度軽く押してお話しください。"
]

async def extract_keywords_from_conversation(conversation_history: List[str]) -> List[str]:
    """
    // AIを使って会話履歴から図書館検索に適したキーワードを3つ抽出します。
    """
    if not conversation_history: return []
    try:
        keyword_prompt = "ユーザーが言及した単語をそのまま抽出してください。カンマ区切りで最大3つまで。"
        user_messages = [msg for i, msg in enumerate(conversation_history) if i % 2 == 0]
        all_user_text = " | ".join(user_messages)
        response = await client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": keyword_prompt},
                {"role": "user", "content": f"会話内容: {all_user_text}"}
            ],
            max_tokens=100, temperature=0.3
        )
        keywords_text = response.choices[0].message.content.strip()
        keywords = [k.strip() for k in keywords_text.split(',') if k.strip()][:3]
        logger.info(f"✅ [Conversation Keywords] Extracted: {keywords}")
        return keywords
    except Exception as e:
        logger.error(f"AI keyword extraction from conversation failed: {e}")
        return []

async def generate_chat_response_and_keywords(message: str, conversation_history: List[str]) -> Tuple[str, List[str]]:
    """
    // OpenAI APIを使用して、会話の応答と検索キーワードの両方を生成します。
    """
    # 1. AIに応答を生成させる
    messages_for_response = [{"role": "system", "content": SYSTEM_PROMPT_CHAT}]
    for i, msg in enumerate(conversation_history):
        role = "user" if i % 2 == 0 else "assistant"
        messages_for_response.append({"role": role, "content": msg})
    messages_for_response.append({"role": "user", "content": message})

    try:
        response_completion = await client.chat.completions.create(
            model="gpt-4-turbo", messages=messages_for_response, max_tokens=500, temperature=0.7
        )
        assistant_response = response_completion.choices[0].message.content.strip()
    except Exception as e:
        logger.error(f"Error generating chat response: {e}")
        assistant_response = "申し訳ございません。応答の生成中にエラーが発生しました。"

    # 2. 生成された応答を含む完全な履歴で、キーワードを再抽出する
    current_full_history = conversation_history + [message, assistant_response]
    keywords = await extract_keywords_from_conversation(current_full_history)

    return assistant_response, keywords

async def ask_openai(past_messages):
    valid_messages = [msg for msg in past_messages if msg.get('content')]
    response = await client.chat.completions.create(
        model="gpt-4o",
        messages=valid_messages
    )
    answer = response.choices[0].message.content.strip()
    return answer
# ===================================================
# 🔊 音声合成関連ロジック
# ===================================================
async def synthesize_speech(text: str, user_id: str) -> str:
    # (音声合成ロジックは変更なし)
    try:
        response = await client.audio.speech.create(
            model="tts-1",
            voice="nova",
            input=text,
        )
        if not os.path.exists("tmp"):
            os.makedirs("tmp")

        unique_id = uuid.uuid4()
        audio_file_path = f"tmp/audio-{user_id}-{unique_id}.mp3"
        response.stream_to_file(audio_file_path)

        s3_key = f"speech-audio/{user_id}-{unique_id}.mp3"
        s3_client.upload_file(audio_file_path, bucket_name, s3_key)
        os.remove(audio_file_path)

        return f"https://{bucket_name}.s3.amazonaws.com/{s3_key}"
    except Exception as e:
        logger.error(f"音声生成失敗: {e}")
        raise HTTPException(status_code=500, detail="音声生成に失敗しました。")

# --- ▼ [追加] OpenAI Whisper STTのための新しいエンドポイント ▼ ---
@router.post("/yokohama/api/stt", tags=["Speech"])
async def transcribe_audio(audio_file: UploadFile = File(...)):
    """
    // フロントエンドから送信された音声ファイルを受け取り、
    // OpenAI Whisper APIを使用して文字起こしを行います。
    """
    # // /tmpディレクトリが存在しない場合は作成
    if not os.path.exists("/tmp"):
        os.makedirs("/tmp")

    file_path = f"/tmp/{uuid.uuid4()}.ogg"
    try:
        # // 一時ファイルとして音声ファイルを保存
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(audio_file.file, buffer)

        # // OpenAI Whisper APIにファイルを送信
        with open(file_path, "rb") as audio:
            transcript_response = await client.audio.transcriptions.create(
                model="whisper-1",
                file=audio,
                response_format="json"
            )

        logger.info(f"Whisper STT successful: {transcript_response.text}")
        return {"transcript": transcript_response.text}

    except Exception as e:
        logger.error(f"Error during transcription: {e}")
        raise HTTPException(status_code=500, detail="音声の文字起こしに失敗しました。")
    finally:
        # // 処理が完了したら一時ファイルを必ず削除
        if os.path.exists(file_path):
            os.remove(file_path)

# ===================================================
# 📡 APIエンドポイント
# ===================================================
@router.get("/health", tags=["System"])
async def health_check():
    return {"status": "healthy"}

@router.post("/yokohama/api/speech", tags=["Speech"])
async def speech(speech_data: SpeechText):
    if not speech_data.text:
        raise HTTPException(status_code=400, detail="Text is required")
    audio_url = await synthesize_speech(speech_data.text, speech_data.chat_token)
    return {"audio_url": audio_url}

@router.post("/yokohama/api/scrape-rankings", tags=["Scraping"])
async def scrape_rakuten_rankings():
    try:
        scraper = RakutenScraper()
        scraped_data = await scraper.scrape()
        return {
            "status": "success",
            "message": "Scraping completed.",
            "scraped_items_count": len(scraped_data)
        }
    except Exception as e:
        logger.error(f"Scraping endpoint failed: {e}")
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/yokohama/api/random-keywords", tags=["Keywords"])
async def get_random_keywords_endpoint(limit: int = 4):
    try:
        keywords = await db_module.get_random_keywords(limit=limit)
        if not keywords:
            return {"keywords": ["村上春樹", "ミステリー", "最新刊", "料理レシピ"]}
        return {"keywords": keywords}
    except Exception as e:
        logger.error(f"Failed to get random keywords: {e}")
        raise HTTPException(status_code=500, detail="Could not fetch keywords.")

@router.post("/yokohama/api/chat/{session_id}", tags=["Chat"])
async def chatbot(session_id: str, request: ChatRequest):
    user_message = request.message

    history_records = await db_module.get_chat_history(session_id)

    conversation_history = []
    for record in history_records:
        if record["user_message"]: conversation_history.append(record["user_message"])
        if record["assistant_response"]: conversation_history.append(record["assistant_response"])

    if not history_records and not user_message:
        response = questions[0] if questions else ""
        status = 1
        keywords = [] # // 最初の挨拶にはキーワードはありません
        await db_module.save_chat_message(session_id, str(status), "", response)
    else:
        # // 応答生成とキーワード抽出を同時に行うロジックを呼び出し
        response, keywords = await generate_chat_response_and_keywords(user_message, conversation_history)
        status = 90
        await db_module.save_chat_message(session_id, str(status), user_message, response)

    # // 応答、キーワード、ステータスをすべて返す
    return {"response": response, "keywords": keywords, "status": status}

@router.get("/yokohama/api/chat/history/{session_id}", tags=["Chat"])
async def get_chat_history_by_session(session_id: str):
    """
    // 指定されたsession_idの全会話履歴をDBから取得して返します。
    """
    history = await db_module.get_chat_history(session_id)
    return {"history": history}

@router.post("/yokohama/api/result/{session_id}", tags=["Chat"])
async def result(session_id: str):
    """
    // 指定されたセッションの会話履歴を要約します。
    """
    history_records = await db_module.get_chat_history(session_id)
    if not history_records:
        raise HTTPException(status_code=404, detail="Chat history not found.")

    system_message = (
            "相談者が自分のことをやさしく再認識できるように、会話の内容を箇条書きで整理してください。"
            "--------------------------------"
            "1. 興味・関心：相談者が話した本やテーマ、気になっていること、関心のある話題"
            "2. 思い出・経験：相談者が思い出したことや過去の経験、心に残っている出来事"
            "3. 感情・気持ち：会話の中で感じたこと、表れた気持ちや反応"
            "4. 新しい発見・気づき：相談者が改めて気づいたことや再認識したこと"
            "5. 前向きなヒント：毎日の生活で試してみたいこと、挑戦してみたいこと、楽しめそうなこと"
    )


    past_messages = [{"role": "system", "content": system_message}]
    for record in history_records:
        if record["user_message"]:
            past_messages.append({"role": "user", "content": record["user_message"]})

    response = await ask_openai(past_messages)

    await db_module.save_chat_message(session_id, "99", "回答結果", response)

    return {'message': "回答結果", 'response': response, 'status': 99}

@router.post("/yokohama/api/clear/{session_id}", tags=["Chat"])
async def clear(session_id: str):
    """
    // 指定されたセッションの会話履歴を削除します。
    """
    await db_module.delete_chat_history(session_id)
    return {"message": f"Chat history for session {session_id} cleared"}

