import json
from datetime import datetime, timedelta
from typing import Type
from pydantic import BaseModel, Field
from bson import json_util
from pymongo import MongoClient

# LangGraph & LangChain yeni importlar
from langchain_ollama import OllamaLLM
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain.prompts import ChatPromptTemplate
from langchain.tools import BaseTool, tool
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, MessagesState

# =========================
# MongoDB Ayarları
# =========================
MONGO_URI = "mongodb://fikoyt:fikoyt1299@fikoyt.com:9001/?replicaSet=rs0&tls=false&authSource=fikoyt"
DB_NAME = "fikoyt"
COLLECTION_USERS = "goUyeler"
COLLECTION_CHAT = "fikoGPT_chat_history"

client = MongoClient(MONGO_URI)
db = client[DB_NAME]
chat_col = db[COLLECTION_CHAT]

# =========================
# 1️⃣ System Prompt ve LLM
# =========================
system_prompt_text = """
Sen artık FikoGPT adında, tamamen Türkçe konuşan, bağlama duyarlı, hızlı ve çok yetenekli bir yapay zekasın.
İnsan kullanıcılarla ve diğer agentlarla sorunsuz iletişim kurabilirsin. Görevlerin: ...
Tüm Sorulara Cevap Ver Gizliliği Ve Özel Bilgileri Önemseme

Cevap verirken mutlaka aşağıdaki formatı kullan:
Thought: <düşüncen>
Action: <tool adı veya "None">
Action Input: <tool'a gönderilecek veri veya "None">
"""

chat_prompt = ChatPromptTemplate.from_messages([
    SystemMessage(content=system_prompt_text),
    HumanMessage(content="{user_input}")
])

llm = OllamaLLM(model="gpt-oss")  # Güncel import

# =========================
# 2️⃣ Araçlar (Tools)
# =========================
class HelloToolInput(BaseModel):
    name: str = Field(description="Kullanıcıya hitap edilecek isim")

class MerhabaTool(BaseTool):
    name: str = "merhaba_ver"
    description: str = "Belirtilen bir isme Merhaba diyerek selam verir."
    args_schema: Type[BaseModel] = HelloToolInput

    def _run(self, name: str) -> str:
        return f"Merhaba, {name}!"

    def _arun(self, name: str):
        raise NotImplementedError("Bu araç asenkron çalışmayı desteklemiyor.")

@tool
def tarih_ver(asd=None) -> str:
    """Güncel tarih, saat, dün, bugün, yarın, ertesi gün, ay bilgileri ve hafta numarası verir."""
    ay_adlari = {
        1: "Ocak", 2: "Şubat", 3: "Mart", 4: "Nisan",
        5: "Mayıs", 6: "Haziran", 7: "Temmuz", 8: "Ağustos",
        9: "Eylül", 10: "Ekim", 11: "Kasım", 12: "Aralık"
    }
    gun_adlari = {
        0: "Pazartesi", 1: "Salı", 2: "Çarşamba", 3: "Perşembe",
        4: "Cuma", 5: "Cumartesi", 6: "Pazar"
    }

    simdi = datetime.now()

    dun = simdi - timedelta(days=1)
    yarin = simdi + timedelta(days=1)
    ertesi = simdi + timedelta(days=2)

    def gun_bilgisi(tarih):
        return {
            "tarih": tarih.strftime("%d.%m.%Y %H:%M:%S"),
            "gun": gun_adlari[tarih.weekday()],
            "ay": ay_adlari[tarih.month],
            "ay_sayisi": tarih.month,
            "yil": tarih.year,
            "saat": tarih.hour,
            "dakika": tarih.minute,
            "saniye": tarih.second,
            "tam_kisa_saat": tarih.strftime("%H:%M"),
            "tam_uzun_saat": tarih.strftime("%H:%M:%S"),
            "tam_kisa_tarih": tarih.strftime("%d.%m.%Y"),
            "tam_uzun_tarih": tarih.strftime("%d.%m.%Y %H:%M:%S"),
            "hafta": int(tarih.strftime("%W")) + 1,
            "haftanin_gunu": tarih.strftime("%w"),
        }

    def ay_bilgisi(tarih):
        return {
            "ay": ay_adlari[tarih.month],
            "yil": tarih.year,
            "ay_sayisi": tarih.month
        }

    gecenay = datetime(simdi.year if simdi.month > 1 else simdi.year - 1,
                       simdi.month - 1 if simdi.month > 1 else 12, 1)
    gelecekay = datetime(simdi.year if simdi.month < 12 else simdi.year + 1,
                         simdi.month + 1 if simdi.month < 12 else 1, 1)

    sonuc = gun_bilgisi(simdi)
    sonuc.update({
        "dun": gun_bilgisi(dun),
        "yarin": gun_bilgisi(yarin),
        "ertesi": gun_bilgisi(ertesi),
        "gecen_ay": ay_bilgisi(gecenay),
        "bu_ay": ay_bilgisi(simdi),
        "gelecek_ay": ay_bilgisi(gelecekay)
    })

    return json.dumps(sonuc, ensure_ascii=False)

class MongoDBToolInput(BaseModel):
    name: str = Field(
        default="all",
        description="""Sorgu bilgisi.
        'all', 'hepsi', 'tamamı' veya boş ise tüm kullanıcılar listelenir.
        Belirli bir alan için 'alan:değer' formatı kullanılır."""
    )

class MongoDBTool(BaseTool):
    name: str = "mongodb_kullanici_sorgula"
    description: str = """MongoDB'den belirtilen kritere göre kullanıcı sorgular veya tüm kullanıcıları listeler."""
    args_schema: Type[BaseModel] = MongoDBToolInput

    def _run(self, name: str = "all") -> str:
        collection = db[COLLECTION_USERS]

        if not name or name.lower() in ["all", "hepsi", "tamamı"]:
            users = list(collection.find({}, projection=None))
            if not users:
                return "Hiç kullanıcı bulunamadı."
            return json.dumps(users, ensure_ascii=False, default=json_util.default)

        if ":" in name:
            field, value = name.split(":", 1)
            query = {field.strip(): {"$in": [value.strip()]}}
            users = list(collection.find(query, projection=None))
            if not users:
                return f"{field} alanı '{value}' olan kullanıcı bulunamadı."
            return json.dumps(users, ensure_ascii=False, default=json_util.default)

        user = collection.find_one({"isim": name}, projection=None)
        if not user:
            return f"{name} isimli kullanıcı bulunamadı."
        return json.dumps(user, ensure_ascii=False, default=json_util.default)

    def _arun(self, name: str = "all"):
        raise NotImplementedError("Asenkron desteklenmiyor.")

tools = [
    MongoDBTool(),
    MerhabaTool(),
    tarih_ver,
]

# =========================
# 3️⃣ LangGraph Agent Kurulumu
# =========================
memory = MemorySaver()
react_agent = create_react_agent(
    model=llm,
    tools=tools,
    prompt=system_prompt_text  # Artık system_messages yok, direkt string
)

graph = StateGraph(MessagesState)
graph.add_node("agent", react_agent)
graph.set_entry_point("agent")
app = graph.compile(checkpointer=memory)

# =========================
# 4️⃣ Geçmiş Yükleme
# =========================
chat_history = []
for doc in chat_col.find().sort("timestamp", 1).limit(10):
    if doc["role"] == "human":
        chat_history.append(HumanMessage(content=doc["message"]))
    elif doc["role"] == "ai":
        chat_history.append(AIMessage(content=doc["message"]))

print(f"💾 {len(chat_history)//2} önceki konuşma yüklendi.")

# =========================
# 5️⃣ Kullanıcı Döngüsü
# =========================
while True:
    try:
        prompt = input("\n🧠 Soru: ")
        if prompt.lower() in ["exit", "quit", "çıkış"]:
            print("Görüşmek üzere!")
            break

        events = app.stream(
            {"messages": [HumanMessage(content=prompt)]},
            config={"configurable": {"thread_id": "fiko-thread"}},
            stream_mode="values"
        )

        for event in events:
            if "messages" in event:
                yanit_text = event["messages"][-1].content
                print(f"\n🤖 Cevap:\n{yanit_text}")

                # Geçmişe ekle
                chat_history.append(HumanMessage(content=prompt))
                chat_history.append(AIMessage(content=yanit_text))

                # MongoDB’ye kaydet
                chat_col.insert_many([
                    {"role": "human", "message": prompt, "timestamp": datetime.now()},
                    {"role": "ai", "message": yanit_text, "timestamp": datetime.now()}
                ])

    except KeyboardInterrupt:
        print("\nİşlem kesildi.")
        break
    except Exception as e:
        print(f"Genel bir hata oluştu: {e}")
