import json
from langchain_community.llms import Ollama
from langchain.agents import initialize_agent, AgentType
from langchain.tools import BaseTool, tool
from langchain.schema import HumanMessage, AIMessage, SystemMessage
from langchain.prompts.chat import ChatPromptTemplate
import os
from typing import Type
from pydantic import BaseModel, Field
from datetime import datetime, timedelta
from bson import json_util
from pymongo import MongoClient

# =========================
# 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 = Ollama(model="gpt-oss")

# =========================
# 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:
    """
    Bu araç kullanıcıya güncel tarih ve saati verir.
    - Gün, ay, yıl, saat, dakika, saniye bilgilerini içerir.
    - Dün, bugün, yarın ve ertesi gün bilgilerini içerir.
    - Geçen ay, bu ay ve gelecek ay bilgilerini içerir.
    - Yılın kaçıncı haftasında olduğunu da içerir.
    """
    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)

from typing import Type
from pydantic import BaseModel, Field
from langchain.tools import BaseTool
import json
from bson import json_util

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 (örn: 'email:test@example.com')."""
    )

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]

        # Tüm kullanıcılar
        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)

        # Belirli alan üzerinden sorgu (alan:değer formatı)
        if ":" in name:
            field, value = name.split(":", 1)
            field = field.strip()
            value = value.strip()
            query = {field:{ "$in":[value]}}
            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)

        # Sadece isim ile sorgu (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️⃣ Agent Kurulumu
# =========================
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
    max_iterations=5,
    verbose=True,
    handle_parsing_errors=True
)

# =========================
# 4️⃣ Geçmiş Yükleme
# =========================
chat_history = []
for doc in chat_col.find().sort("timestamp", 1):
    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

        # chat_prompt'u kullanarak mesajları hazırla
        prompt_messages = chat_prompt.format_prompt(user_input=prompt).to_messages()

        # 🔹 invoke ile LLM/agent çalıştırma
        yanit = agent.invoke(
            input={"input": prompt, "chat_history": chat_history}
        )

        yanit_text = yanit if isinstance(yanit, str) else str(yanit)

        # Geçmişi güncelle
        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.utcnow()},
            {"role": "ai", "message": yanit_text, "timestamp": datetime.utcnow()}
        ])

        print(f"\n🤖 Cevap:\n{yanit_text}")

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