#!/usr/bin/python
# -*- coding: utf-8 -*-

# --------------------------------------------------------------------------- #
#                                                                             #
#    Plugin for iSida Jabber Bot                                              #
#    AI Assistant with Ollama + NLU                                          #
#                                                                             #
#    This program is free software: you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, either version 3 of the License, or        #
#    (at your option) any later version.                                      #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.    #
#                                                                             #
# --------------------------------------------------------------------------- #

import re
import json
import random
import time
import urllib.parse
import urllib.request

AI_CONFIG = {
    'enabled': True,
    'respond_to_mention': True,
    'max_response_length': 512,
    'model': 'mistral',
    'ollama_host': 'http://192.168.31.2:11434',
    'num_predict': 200,
    'temperature': 0.1,
}

conversation_cache = {}
last_message_time = {}
user_cache = {}
owner_jid = None

# ============== ИНИЦИАЛИЗАЦИЯ ==============

def init_owner():
    global owner_jid
    try:
        owner_jid = SuperAdmin.lower()
    except:
        owner_jid = None

# ============== ТРАНСЛИТЕРАЦИЯ ==============

RUS_TO_LAT = {
    'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo',
    'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm',
    'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u',
    'ф': 'f', 'х': 'kh', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'shch',
    'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya',
    'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo',
    'Ж': 'Zh', 'З': 'Z', 'И': 'I', 'Й': 'Y', 'К': 'K', 'Л': 'L', 'М': 'M',
    'Н': 'N', 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U',
    'Ф': 'F', 'Х': 'Kh', 'Ц': 'Ts', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Shch',
    'Ъ': '', 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya'
}

def transliterate(text):
    result = []
    for char in text:
        result.append(RUS_TO_LAT.get(char, char))
    return ''.join(result)

# ============== СЛОВАРИ ==============

CURRENCY_ALIASES = {
    'доллар': 'USD', 'бакс': 'USD', 'зелень': 'USD', '$': 'USD',
    'евро': 'EUR', 'euro': 'EUR', '€': 'EUR',
    'рубль': 'RUB', 'деревянный': 'RUB', 'руб': 'RUB', '₽': 'RUB',
    'гривна': 'UAH', 'грн': 'UAH', '₴': 'UAH',
    'фунт': 'GBP', '£': 'GBP',
    'йена': 'JPY', '¥': 'JPY',
    'юань': 'CNY',
    'франк': 'CHF',
}

# ============== ФУНКЦИИ ==============

def get_weather(city='Engels'):
    try:
        city_lat = transliterate(city)
        url = f'https://wttr.in/{urllib.parse.quote(city_lat)}?lang=ru&format=%C+%t+%w+%h'
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = resp.read().decode('utf-8').strip()
            if not data or data.startswith('<!DOCTYPE'):
                return None
            parts = data.split()
            if len(parts) >= 4:
                weather = parts[0]
                temp = parts[1]
                wind = parts[2]
                humid = parts[3]
                return f"{weather}, {temp}, ветер {wind}, влажность {humid}"
            return data
    except:
        return None

def calculate(expression):
    try:
        expr = expression.replace(' ', '')
        expr = expr.replace('плюс', '+').replace('минус', '-')
        expr = expr.replace('умножить', '*').replace('разделить', '/')
        expr = expr.replace('делить', '/')
        if not re.match(r'^[\d+\-*/().]+$', expr):
            return None
        result = eval(expr, {"__builtins__": {}}, {})
        return str(round(float(result), 2))
    except:
        return None

def get_currency_rate(from_cur, to_cur):
    try:
        from_cur = CURRENCY_ALIASES.get(from_cur.lower(), from_cur.upper())
        to_cur = CURRENCY_ALIASES.get(to_cur.lower(), to_cur.upper())
        url = f'https://api.exchangerate-api.com/v4/latest/{from_cur}'
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
            rate = data['rates'].get(to_cur)
            if rate:
                return rate
            return None
    except:
        return None

def convert_currency(amount, from_cur, to_cur):
    rate = get_currency_rate(from_cur, to_cur)
    if rate:
        result = float(amount) * rate
        from_name = from_cur.upper()
        to_name = to_cur.upper()
        return f"{amount} {from_name} = {round(result, 2)} {to_name}"
    return None

def get_currency_rate_only(currency):
    """Получить курс одной валюты к рублю"""
    currency = CURRENCY_ALIASES.get(currency.lower(), currency.upper())
    rate = get_currency_rate(currency, 'RUB')
    if rate:
        return f"1 {currency} = {rate} RUB"
    return None

# ============== ПАРСИНГ ЕСТЕСТВЕННОГО ЯЗЫКА ==============

def parse_natural_language(text):
    text_lower = text.lower()

    # ===== ПОГОДА =====
    weather_patterns = [
        r'(?:какая\s+)?погода\s+(?:в|в городе)?\s*([а-яА-Яa-zA-Z-]+)',
        r'(?:weather|погода)\s+(?:in)?\s*([а-яА-Яa-zA-Z-]+)',
        r'(?:погода)\s*([а-яА-Яa-zA-Z-]+)',
    ]
    for pattern in weather_patterns:
        match = re.search(pattern, text, re.I)
        if match:
            return {'tool': 'get_weather', 'city': match.group(1).strip()}

    # ===== КАЛЬКУЛЯТОР =====
    calc_patterns = [
        r'(?:посчитай|сколько\s+будет|сколько|найди)\s+([\d+\-*/().\s]+)',
        r'(?:calculate|calc)\s+([\d+\-*/().\s]+)',
        r'^([\d+\-*/().\s]+)$',
    ]
    for pattern in calc_patterns:
        match = re.search(pattern, text, re.I)
        if match:
            expr = match.group(1).strip()
            expr = expr.replace('плюс', '+').replace('минус', '-')
            expr = expr.replace('умножить', '*').replace('разделить', '/')
            expr = expr.replace('делить', '/')
            expr = re.sub(r'\s+', '', expr)
            if re.match(r'^[\d+\-*/().]+$', expr):
                return {'tool': 'calculate', 'expression': expr}

    # ===== ВАЛЮТЫ (конвертация) =====
    currency_patterns = [
        r'(?:сколько|курс)\s+([\d]+\.?\d*)?\s*([а-яa-z$€₽₴£¥]+)\s+(?:в|to)\s+([а-яa-z$€₽₴£¥]+)',
        r'(?:конверт|конвертир|переведи)\s+([\d]+\.?\d*)?\s*([а-яa-z$€₽₴£¥]+)\s+(?:в|to)\s*([а-яa-z$€₽₴£¥]+)',
        r'([\d]+\.?\d*)\s*([а-яa-z$€₽₴£¥]+)\s*(?:в|to)\s*([а-яa-z$€₽₴£¥]+)',
    ]
    for pattern in currency_patterns:
        match = re.search(pattern, text, re.I)
        if match:
            amount = match.group(1) if match.group(1) else None
            from_cur = match.group(2)
            to_cur = match.group(3)
            if amount:
                return {'tool': 'convert_currency', 'amount': amount, 'from': from_cur, 'to': to_cur}
            else:
                return {'tool': 'get_currency_rate', 'from': from_cur, 'to': to_cur}

    # ===== КУРС ОДНОЙ ВАЛЮТЫ =====
    rate_patterns = [
        r'(?:курс)\s*([а-яa-z$€₽₴£¥]+)',
    ]
    for pattern in rate_patterns:
        match = re.search(pattern, text, re.I)
        if match:
            currency = match.group(1)
            return {'tool': 'get_currency_rate_only', 'currency': currency}

    return None

# ============== AI ==============

def get_ai_response(room, jid, nick, messages, max_length=512):
    try:
        last_msg = messages[-1]['content'] if messages else ''

        # Парсим естественный язык
        intent = parse_natural_language(last_msg)

        if intent:
            tool = intent.get('tool')
            if tool == 'get_weather':
                city = intent.get('city', 'Engels')
                result = get_weather(city)
                if result:
                    return f"Погода в {city}: {result}"
                else:
                    # Пробуем без транслитерации
                    city_lat = transliterate(city)
                    return f"Не могу найти погоду для {city} (попробуйте на английском: {city_lat})"

            elif tool == 'calculate':
                expr = intent.get('expression', '')
                result = calculate(expr)
                return f"Ответ: {result}" if result else f"Не могу вычислить: {expr}"

            elif tool == 'convert_currency':
                amount = intent.get('amount')
                from_cur = intent.get('from')
                to_cur = intent.get('to')
                result = convert_currency(amount, from_cur, to_cur)
                return result if result else f"Не могу найти курс {from_cur} к {to_cur}"

            elif tool == 'get_currency_rate':
                from_cur = intent.get('from')
                to_cur = intent.get('to')
                result = convert_currency(1, from_cur, to_cur)
                return result if result else f"Не могу найти курс {from_cur} к {to_cur}"

            elif tool == 'get_currency_rate_only':
                currency = intent.get('currency')
                result = get_currency_rate_only(currency)
                return result if result else f"Не могу найти курс {currency}"

        # Если не распознали — отправляем в модель
        payload = {
            "model": AI_CONFIG.get('model', 'mistral'),
            "messages": [
                {"role": "system", "content": "Ты — бот-помощник Isida. Отвечай кратко, дружелюбно. Если не знаешь — скажи 'Не знаю'. Отвечай на русском."},
                {"role": "user", "content": last_msg}
            ],
            "stream": False,
            "options": {
                "num_predict": max_length,
                "temperature": AI_CONFIG.get('temperature', 0.1),
            }
        }

        ollama_host = AI_CONFIG.get('ollama_host', 'http://192.168.31.2:11434')
        req = urllib.request.Request(
            f'{ollama_host}/api/chat',
            data=json.dumps(payload).encode('utf-8'),
            headers={'Content-Type': 'application/json'}
        )

        with urllib.request.urlopen(req, timeout=60) as resp:
            result = json.loads(resp.read().decode('utf-8'))
            answer = result.get('message', {}).get('content', '')
            return answer if answer else 'Не знаю 😅'

    except Exception as e:
        return 'Не знаю 😅'

# ============== ОБРАБОТКА ==============

def ai_process_message(room, jid, nick, type, text):
    if not AI_CONFIG['enabled'] or type != 'groupchat':
        return False

    bot_nick = get_xnick(room)
    if nick == bot_nick:
        return False

    is_mentioned = bot_nick.lower() in text.lower()

    if not is_mentioned:
        return False

    current_time = time.time()
    if room in last_message_time and current_time - last_message_time[room] < 2:
        return False
    last_message_time[room] = current_time

    clean_text = text
    if bot_nick.lower() in clean_text.lower():
        clean_text = re.sub(rf'{bot_nick}[,:\s]*', '', clean_text, flags=re.I).strip()

    if not clean_text:
        return False

    messages = [{'role': 'user', 'content': clean_text}]
    answer = get_ai_response(room, jid, nick, messages, AI_CONFIG['max_response_length'])

    if answer:
        send_msg(type, room, '', answer)
        return True

    return False

def ai_command(type, jid, nick, text):
    if not text:
        send_msg(type, jid, nick, L('Использование: ai <текст>', '%s/%s' % (jid, nick)))
        return

    messages = [{'role': 'user', 'content': text}]
    answer = get_ai_response(jid, jid, nick, messages, 512)
    send_msg(type, jid, nick, answer if answer else L('Не знаю', '%s/%s' % (jid, nick)))

def ai_config(type, jid, nick, text):
    if not text:
        msg = L('Настройки AI:\n', '%s/%s' % (jid, nick))
        for key, value in AI_CONFIG.items():
            msg += '%s: %s\n' % (key, value)
        send_msg(type, jid, nick, msg)
        return

    parts = text.strip().split(' ', 1)
    if len(parts) != 2:
        send_msg(type, jid, nick, L('Использование: aiconfig <ключ> <значение>', '%s/%s' % (jid, nick)))
        return

    key, value = parts[0], parts[1]
    if key not in AI_CONFIG:
        send_msg(type, jid, nick, L('Неизвестный ключ: %s', '%s/%s' % (jid, nick)) % key)
        return

    if value.lower() in ['true', 'on', 'yes', '1']:
        AI_CONFIG[key] = True
    elif value.lower() in ['false', 'off', 'no', '0']:
        AI_CONFIG[key] = False
    else:
        try:
            AI_CONFIG[key] = int(value)
        except:
            AI_CONFIG[key] = value

    send_msg(type, jid, nick, L('Обновлено: %s = %s', '%s/%s' % (jid, nick)) % (key, AI_CONFIG[key]))

# ============== РЕГИСТРАЦИЯ ==============

global execute, message_act_control

init_owner()

execute = [
    (3, 'ai', ai_command, 2, 'Chat with AI\nUsage: ai <text>'),
    (5, 'aiconfig', ai_config, 5, 'Configure AI\nUsage: aiconfig <key> <value>'),
]

message_act_control = [ai_process_message]
