#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Капкан — инструмент проверки ссылок на фишинг и безопасность.
Графический интерфейс на базе Tkinter.
Разработано @SYSHOP_CHANNEL
"""

import sys
import re
import urllib.parse
import threading
from difflib import SequenceMatcher

try:
    import requests
except ImportError:
    print("❌ Ошибка: библиотека 'requests' не установлена.")
    print("   Установите её командой: pip install requests")
    sys.exit(1)

# Попытка импорта tkinter (в Linux может требоваться python3-tk)
try:
    import tkinter as tk
    from tkinter import messagebox, scrolledtext
except ImportError:
    print("❌ Ошибка: модуль tkinter не найден.")
    print("   Установите пакет python3-tk (sudo apt install python3-tk) или аналогичный для вашей ОС.")
    sys.exit(1)


# ==============================================================================
# НАСТРОЙКИ (можно вынести в отдельный конфиг при желании)
# ==============================================================================

# Список известных сервисов сокращения ссылок (домены)
SHORTENER_DOMAINS = [
    "bit.ly", "tinyurl.com", "clck.ru", "t.co", "goo.gl",
    "ow.ly", "is.gd", "buff.ly", "shorte.st", "bc.vc",
    "j.mp", "v.gd", "0rz.tw", "1link.in", "2.gp"
]

# Подозрительные доменные зоны (TLD), часто используемые фишерами
SUSPICIOUS_TLDS = [".tk", ".ml", ".ga", ".cf", ".gq", ".xyz"]

# Стоп-слова, наличие которых в URL повышает риск фишинга
STOP_WORDS = [
    "login", "verify", "secure", "bank", "free",
    "gift", "giveaway", "bonus", "telegram-bot"
]

# Эталонные домены популярных сервисов для проверки тайпосквоттинга
TRUSTED_DOMAINS = {
    "google.com": "Google",
    "telegram.org": "Telegram",
    "vk.com": "VK"
}

# Таблица замен символов, используемых для маскировки (0->o, 1->l и т.д.)
CHAR_REPLACEMENTS = {
    '0': 'o', '1': 'l', '3': 'e', '4': 'a', '5': 's',
    '@': 'a', '!': 'i', '$': 's', '6': 'b', '8': 'b',
    '9': 'g', '(': 'c'
}


# ==============================================================================
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ (логика проверок без GUI)
# ==============================================================================

def extract_domain(url: str) -> str:
    """Извлекает чистое доменное имя (без www. и порта) из URL."""
    parsed = urllib.parse.urlparse(url)
    hostname = parsed.hostname or ""
    if hostname.startswith("www."):
        hostname = hostname[4:]
    return hostname.lower()


def levenshtein_distance(s1: str, s2: str) -> int:
    """Классическое расстояние Левенштейна (редакционное расстояние)."""
    if len(s1) < len(s2):
        return levenshtein_distance(s2, s1)
    if len(s2) == 0:
        return len(s1)
    previous_row = range(len(s2) + 1)
    for i, c1 in enumerate(s1):
        current_row = [i + 1]
        for j, c2 in enumerate(s2):
            insertions = previous_row[j + 1] + 1
            deletions = current_row[j] + 1
            substitutions = previous_row[j] + (c1 != c2)
            current_row.append(min(insertions, deletions, substitutions))
        previous_row = current_row
    return previous_row[-1]


def normalize_domain(domain: str) -> str:
    """Приводит домен к нижнему регистру и заменяет типичные символы-заменители."""
    domain = domain.lower()
    for old, new in CHAR_REPLACEMENTS.items():
        domain = domain.replace(old, new)
    return domain


def expand_short_url(url: str) -> str | None:
    """Пытается развернуть короткую ссылку, следуя всем редиректам."""
    try:
        response = requests.get(url, allow_redirects=True, timeout=5)
        return response.url
    except requests.RequestException as e:
        return None


def check_tld(domain: str) -> list[str]:
    """Проверяет, заканчивается ли домен на одну из подозрительных зон."""
    found = []
    for tld in SUSPICIOUS_TLDS:
        if domain.endswith(tld):
            found.append(tld)
    return found


def check_stop_words(url: str) -> list[str]:
    """Ищет стоп-слова в URL (регистр не учитывается)."""
    url_lower = url.lower()
    found = []
    for word in STOP_WORDS:
        if word in url_lower:
            found.append(word)
    return found


def check_typosquatting(domain: str) -> list[tuple[str, str]]:
    """Проверяет, не маскируется ли домен под популярный сервис."""
    suspicious = []
    normalized_input = normalize_domain(domain)

    for trusted_domain, brand in TRUSTED_DOMAINS.items():
        if domain == trusted_domain:
            continue
        normalized_trusted = normalize_domain(trusted_domain)

        if normalized_input == normalized_trusted:
            suspicious.append((domain, brand))
            continue

        dist = levenshtein_distance(domain, trusted_domain)
        if dist <= 2:
            ratio = SequenceMatcher(None, domain, trusted_domain).ratio()
            if ratio > 0.85:
                suspicious.append((domain, brand))
    return suspicious


# ==============================================================================
# ГЛАВНАЯ ЛОГИКА ПРОВЕРКИ (вызывается в отдельном потоке)
# ==============================================================================

def perform_checks(url: str) -> str:
    """
    Выполняет все проверки для переданного URL.
    Возвращает строку с форматированным отчётом (может содержать эмодзи).
    """
    lines = []
    warnings = []

    # 1. Проверка и разворачивание коротких ссылок
    parsed_url = urllib.parse.urlparse(url)
    hostname = parsed_url.hostname or ""

    is_shortener = any(hostname.endswith(short) for short in SHORTENER_DOMAINS)

    lines.append("🔍 Проверка короткой ссылки:")
    if is_shortener:
        lines.append(f"   Сервис сокращения: {hostname}")
        expanded = expand_short_url(url)
        if expanded and expanded != url:
            lines.append(f"   🎯 Реальный адрес: {expanded}")
            url = expanded
            # Обновляем hostname для дальнейших проверок
            parsed_url = urllib.parse.urlparse(url)
            hostname = parsed_url.hostname or ""
        else:
            lines.append("   ⚠️  Не удалось развернуть ссылку. Будьте осторожны!")
            warnings.append("Не удалось развернуть короткую ссылку.")
    else:
        lines.append("   ✅ Ссылка не является короткой (из известных сервисов).")

    # Извлекаем чистый домен
    domain = extract_domain(url)

    # 2. Проверка доменной зоны
    lines.append("\n🌐 Проверка доменной зоны:")
    suspicious_tlds = check_tld(domain)
    if suspicious_tlds:
        msg = f"⚠️  Подозрительная зона: {', '.join(suspicious_tlds)}. Домен: {domain}"
        lines.append(f"   {msg}")
        warnings.append(msg)
    else:
        lines.append("   ✅ Доменная зона не входит в список подозрительных.")

    # 3. Поиск стоп-слов
    lines.append("\n🚫 Поиск стоп-слов:")
    found_stop_words = check_stop_words(url)
    if found_stop_words:
        msg = f"⚠️  Найдены слова: {', '.join(found_stop_words)}"
        lines.append(f"   {msg}")
        warnings.append(msg)
    else:
        lines.append("   ✅ Опасные слова не обнаружены.")

    # 4. Тайпосквоттинг
    lines.append("\n🕵️ Проверка на тайпосквоттинг:")
    typos = check_typosquatting(domain)
    if typos:
        for fake_domain, brand in typos:
            msg = f"⚠️  Домен {fake_domain} похож на {brand} (возможна подделка)"
            lines.append(f"   {msg}")
            warnings.append(msg)
    else:
        lines.append("   ✅ Домен не похож на Google, Telegram, VK.")

    # Итог
    lines.append("\n" + "=" * 50)
    if warnings:
        lines.append("⚠️  ВНИМАНИЕ! Ссылка может быть ОПАСНОЙ!")
        lines.append("   Подозрительные признаки:")
        for w in warnings:
            lines.append(f"   • {w}")
    else:
        lines.append("✅ Ссылка выглядит БЕЗОПАСНОЙ.")
        lines.append("   Явных признаков фишинга не обнаружено.")
    lines.append("=" * 50)

    return "\n".join(lines)


# ==============================================================================
# ГРАФИЧЕСКИЙ ИНТЕРФЕЙС НА TKINTER
# ==============================================================================

class PhishingCheckerApp:
    """Главный класс приложения с графическим интерфейсом."""

    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("Капкан — проверка ссылок на фишинг")
        self.root.geometry("750x650")
        self.root.resizable(True, True)

        # Настройка цветовой схемы (хакерский/защитный стиль)
        self.bg_color = "#1e1e1e"
        self.fg_color = "#00ff00"       # ярко-зелёный
        self.entry_bg = "#2d2d2d"
        self.text_bg = "#2d2d2d"
        self.root.configure(bg=self.bg_color)

        # Создание интерфейса
        self.create_widgets()

        # Атрибут для хранения потока проверки (чтобы избежать повторных запусков)
        self.check_thread = None

    def create_widgets(self):
        """Создаёт и размещает все элементы интерфейса."""

        # Заголовок
        header = tk.Label(
            self.root,
            text="🛡️  К А П К А Н  🛡️",
            font=("Courier", 18, "bold"),
            fg=self.fg_color,
            bg=self.bg_color
        )
        header.pack(pady=10)

        sub_header = tk.Label(
            self.root,
            text="Инструмент проверки ссылок на фишинг",
            font=("Courier", 11),
            fg="#aaaaaa",
            bg=self.bg_color
        )
        sub_header.pack()

        # Рамка для ввода URL
        input_frame = tk.Frame(self.root, bg=self.bg_color)
        input_frame.pack(pady=15, padx=20, fill=tk.X)

        url_label = tk.Label(
            input_frame,
            text="🔗 Введите ссылку для проверки:",
            font=("Courier", 11),
            fg=self.fg_color,
            bg=self.bg_color
        )
        url_label.pack(anchor=tk.W)

        self.url_entry = tk.Entry(
            input_frame,
            font=("Courier", 11),
            bg=self.entry_bg,
            fg=self.fg_color,
            insertbackground=self.fg_color,
            relief=tk.FLAT,
            bd=5
        )
        self.url_entry.pack(fill=tk.X, ipady=4, pady=5)
        # Привязка клавиши Enter к запуску проверки
        self.url_entry.bind("<Return>", lambda event: self.start_check())

        # Кнопка проверки
        self.check_button = tk.Button(
            input_frame,
            text="🔍 Проверить",
            font=("Courier", 11, "bold"),
            bg="#333333",
            fg=self.fg_color,
            activebackground="#555555",
            activeforeground="white",
            relief=tk.RAISED,
            bd=3,
            command=self.start_check
        )
        self.check_button.pack(pady=10)

        # Область вывода результатов (с прокруткой)
        result_frame = tk.Frame(self.root, bg=self.bg_color)
        result_frame.pack(pady=10, padx=20, fill=tk.BOTH, expand=True)

        result_label = tk.Label(
            result_frame,
            text="📋 Результаты проверки:",
            font=("Courier", 11),
            fg=self.fg_color,
            bg=self.bg_color,
            anchor=tk.W
        )
        result_label.pack(anchor=tk.W)

        self.result_text = scrolledtext.ScrolledText(
            result_frame,
            wrap=tk.WORD,
            font=("Courier", 10),
            bg=self.text_bg,
            fg=self.fg_color,
            insertbackground=self.fg_color,
            relief=tk.FLAT,
            bd=5,
            state=tk.DISABLED
        )
        self.result_text.pack(fill=tk.BOTH, expand=True)

        # Нижняя подпись автора
        author_label = tk.Label(
            self.root,
            text="Разработано @SYSHOP_CHANNEL",
            font=("Courier", 9, "italic"),
            fg="#666666",
            bg=self.bg_color
        )
        author_label.pack(side=tk.BOTTOM, pady=5)

    def start_check(self):
        """Запускает проверку URL в отдельном потоке, чтобы не блокировать GUI."""
        url = self.url_entry.get().strip()
        if not url:
            messagebox.showwarning("Пустой URL", "Пожалуйста, введите ссылку для проверки.")
            return

        # Блокируем кнопку на время проверки
        self.check_button.config(state=tk.DISABLED, text="⏳ Проверка...")
        self.clear_results()
        self.append_to_results("Выполняется проверка...\n")

        # Запуск в отдельном потоке
        self.check_thread = threading.Thread(target=self.run_checks, args=(url,), daemon=True)
        self.check_thread.start()

    def run_checks(self, url: str):
        """Фоновая задача: выполняет проверки и обновляет GUI через after."""
        try:
            result = perform_checks(url)
        except Exception as e:
            result = f"❌ Во время проверки произошла ошибка:\n{str(e)}"
        # Безопасное обновление интерфейса из главного потока
        self.root.after(0, self.display_results, result)

    def display_results(self, result: str):
        """Отображает итоговый отчёт в текстовом поле и разблокирует кнопку."""
        self.clear_results()
        self.append_to_results(result)
        self.check_button.config(state=tk.NORMAL, text="🔍 Проверить")

    def clear_results(self):
        """Очищает поле вывода."""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        self.result_text.config(state=tk.DISABLED)

    def append_to_results(self, text: str):
        """Добавляет текст в поле вывода."""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.insert(tk.END, text)
        self.result_text.see(tk.END)
        self.result_text.config(state=tk.DISABLED)


# ==============================================================================
# ЗАПУСК ПРИЛОЖЕНИЯ
# ==============================================================================

if __name__ == "__main__":
    root = tk.Tk()
    app = PhishingCheckerApp(root)
    root.mainloop()