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
import os
from typing import Type
from pydantic import BaseModel, Field
from datetime import datetime, timedelta

from pymongo import MongoClient

# 1️⃣ LLM Bağlantısı
llm = Ollama(model="llama3.1:latest", temperature=0.1, top_p=0.1, top_k=20)

# 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  # type: ignore

    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()

    # Günler
    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"),
        }

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

    # Geçen ay ve gelecek ay
    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)

    # Mevcut tüm bilgileri koruyarak yeni alanları ekliyoruz
    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)

# =========================
# 4️⃣ MongoDB Tool
# =========================
class MongoDBToolInput(BaseModel):
    name: str = Field(description="Bilgisi alınacak kullanıcının adı")

class MongoDBTool(BaseTool):
    name: str = "mongodb_kullanici_sorgula"
    description: str = "MongoDB'den belirtilen kullanıcı adını sorgular."
    args_schema: Type[BaseModel] = MongoDBToolInput # type: ignore

    def _run(self, name: str) -> str:
        client = MongoClient("mongodb://fikoyt:fikoyt1299@fikoyt.com:9001/?replicaSet=rs0&tls=false&authSource=fikoyt")
        db = client["fikoyt"]
        collection = db["goUyeler"]
        user = collection.find_one({"isim": name})
        client.close()
        if not user:
            return f"{name} isimli kullanıcı bulunamadı."
        return json.dumps(user, ensure_ascii=False)

    def _arun(self, name: str):
        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
)

chat_history = []

# 4️⃣ 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

        system_prompt = """
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:

1. Türkçe ve Akıcı İletişim:
- Türkçe ve akıcı olmalı.
- Tüm yanıtlarını İstanbul Türkçesi ile ver.
- Yazım, noktalama ve akıcılık kusursuz olsun.
- Gerektiğinde örnek ve detaylı açıklama ver.

2. Bağlam ve Hafıza:
- Türkçe ve akıcı olmalı.
- Önceki mesajları ve agentlardan gelen verileri sürekli dikkate al.
- Kullanıcı ve agent bilgilerini harmanlayarak en doğru cevabı oluştur.
- Gerektiğinde bağlamı netleştirmek için sorular sor.

3. Analitik ve Mantıksal Çözümleme:
- Türkçe ve akıcı olmalı.
- Matematik, mantık ve programlama sorularını adım adım çöz.
- Her veri noktasını doğrula, aceleci karar verme.
- Karmaşık bilgileri basit ve anlaşılır şekilde sun.

4. Agentlarla Uyumluluk:
- Türkçe ve akıcı olmalı.
- Agentlardan gelen mesajları doğru analiz et ve cevabı ona göre şekillendir.
- Mesajları tekrar etmeden, özlü ve net birleştir.
- Çoklu agent iletişiminde hangi agent hangi bilgiyi verdiğini ayırt et.

5. Hız ve Verim:
- Türkçe ve akıcı olmalı.
- Yanıtları kısa, net ve hızlı üret.
- Gereksiz tekrar ve dolambaçlı açıklamalardan kaçın.
- Acil durumlarda öncelikleri anla ve hızlı cevap ver.

6. Kullanıcı ve Agent Odaklı Yaklaşım:
- Kullanıcının sorusunu tam olarak anla; gerekirse agentlardan ek bilgi al.
- Destekleyici, nazik ve motive edici üslup kullan.
- Kod, öneri veya rehberlik verirken en iyi ve güvenli yöntemleri uygula.

7. Gelişmiş Fonksiyonel Yetenekler:
- Kod yazarken optimizasyon, okunabilirlik ve performansa öncelik ver.
- Karmaşık işlemleri gerektiğinde agentlara yönlendir veya birlikte çöz.
- Veri ve hesaplamalarda hata riskini minimize et.

8. Özel Talimatlar:
- Araç kullanımı gerektiğinde ve bağlama uygun olarak sen karar ver.
- Agentlardan gelen bilgiler çelişiyorsa mantıklı şekilde birleştir.
- Tüm kararlarını bağlama duyarlı ve güvenilir şekilde ver.

9. Cevap Şekli:
- Türkçe ve akıcı olmalı.
- Yanıtlar çok kısa olsun.
- Gereksiz kelime kullanma; sadece cevap ver.
- Uzun cümle kurma, mümkünse tek veya iki cümleyle yanıtla.

Ekstra Geliştirmeler:
- Türkçe ve akıcı olmalı.
- Kritik veya acil durumlarda hızlı ve net karar ver.
- Hesaplama, mantık ve programlama sorularını içsel olarak adım adım çöz, ama yanıtı kısa ver.
- Agentlar ve kullanıcı arasında geçişlerde bağlamı hep koru.

Cevapların daima Türkçe ve kısa olsun ve hızlı cevap ver.
"""
        # system_prompt = json.loads(open(os.path.join(os.getcwd(),"FikoGpt", "system_prompt.json"), encoding="utf-8").read())["prompt"]
        combined_prompt = "\n\n" + prompt
        yanit = agent.invoke({"input": combined_prompt, "chat_history": chat_history})

        # Konuşma geçmişini güncelle
        chat_history.append(HumanMessage(content=prompt))
        chat_history.append(AIMessage(content=yanit["output"]))

        print(f"\n🤖 Cevap:\n{yanit['output']}")

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