import subprocess
import socket
import os
import re
import platform
import uuid
import netifaces


class CyberTools:
    def __init__(self):
        self.os_type = platform.system().lower()

    def check_internet(self):
        try:
            socket.create_connection(("8.8.8.8", 53), timeout=3)
            return "Connected"
        except OSError:
            return "Disconnected"

    def get_local_ip(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
            s.close()
            return ip
        except:
            return "Unknown"

    def get_public_ip(self):
        try:
            import requests

            return requests.get("https://api.ipify.org", timeout=5).text
        except:
            return "Unable to fetch"

    def get_mac_address(self):
        try:
            mac = ":".join(re.findall("..", "%012x" % uuid.getnode()))
            return mac
        except:
            return "Unknown"

    def get_all_interfaces(self):
        try:
            result = []
            for iface in netifaces.interfaces():
                addrs = netifaces.ifaddresses(iface)
                if netifaces.AF_INET in addrs:
                    for addr in addrs[netifaces.AF_INET]:
                        result.append(f"{iface}: {addr['addr']}")
            return "\n".join(result) if result else "No interfaces found"
        except:
            return "Error getting interfaces"

    def port_scan(self, host="localhost", ports="1-1000", timeout=0.1):
        try:
            result = []
            port_list = []

            if "-" in ports:
                start, end = map(int, ports.split("-"))
                port_list = range(start, min(end + 1, 65535))
            else:
                port_list = [int(p.strip()) for p in ports.split(",")]

            common_services = {
                21: "FTP",
                22: "SSH",
                23: "Telnet",
                25: "SMTP",
                53: "DNS",
                80: "HTTP",
                110: "POP3",
                143: "IMAP",
                443: "HTTPS",
                445: "SMB",
                993: "IMAPS",
                995: "POP3S",
                3306: "MySQL",
                3389: "RDP",
                5432: "PostgreSQL",
                5900: "VNC",
                8080: "HTTP-Alt",
            }

            for port in list(port_list)[:1000]:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(timeout)
                if sock.connect_ex((host, port)) == 0:
                    service = common_services.get(port, "Unknown")
                    result.append(f"Port {port}/tcp: OPEN ({service})")
                sock.close()

            return "\n".join(result) if result else "No open ports found"
        except Exception as e:
            return f"Error: {str(e)}"

    def full_port_scan(self, host="localhost", ports="1-10000"):
        return self.port_scan(host, ports, timeout=0.5)

    def quick_scan(self, host="localhost"):
        critical_ports = "21,22,23,25,53,80,110,143,443,445,993,995,3306,3389,5432,8080"
        return self.port_scan(host, critical_ports, timeout=0.05)

    def ping_host(self, host, count=4):
        try:
            param = "-n" if os.name == "nt" else "-c"
            cmd = ["ping", param, str(count), host]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            return result.stdout if result.returncode == 0 else result.stderr
        except Exception as e:
            return f"Error: {str(e)}"

    def traceroute(self, host, max_hops=30):
        try:
            if self.os_type == "windows":
                cmd = ["tracert", "-h", str(max_hops), host]
            else:
                cmd = ["traceroute", "-m", str(max_hops), host]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
            return result.stdout if result.returncode == 0 else result.stderr
        except FileNotFoundError:
            return "Traceroute not installed"
        except Exception as e:
            return f"Error: {str(e)}"

    def arp_scan(self):
        try:
            if self.os_type == "linux":
                result = subprocess.run(["arp", "-a"], capture_output=True, text=True)
                return result.stdout if result.stdout else result.stderr
            elif self.os_type == "windows":
                result = subprocess.run(
                    ["arp", "-a"], capture_output=True, text=True, shell=True
                )
                return result.stdout if result.stdout else result.stderr
        except Exception as e:
            return f"Error: {str(e)}"

    def dns_lookup(self, domain):
        try:
            ip = socket.gethostbyname(domain)
            return f"Domain: {domain}\nIP: {ip}"
        except Exception as e:
            return f"Error: {str(e)}"

    def reverse_dns(self, ip):
        try:
            domain = socket.gethostbyaddr(ip)
            return f"IP: {ip}\nDomain: {domain[0]}"
        except Exception as e:
            return f"Error: {str(e)}"

    def check_dns(self, domain):
        try:
            import dns.resolver

            result = []
            for rtype in ["A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA", "PTR"]:
                try:
                    answers = dns.resolver.resolve(domain, rtype)
                    for rdata in answers:
                        result.append(f"{rtype}: {rdata}")
                except:
                    pass
            return "\n".join(result) if result else "No DNS records found"
        except ImportError:
            return "Install dnspython: pip install dnspython"
        except Exception as e:
            return f"Error: {str(e)}"

    def subdomain_enum(self, domain):
        try:
            import requests

            result = []
            common_subs = [
                "www",
                "mail",
                "ftp",
                "admin",
                "blog",
                "dev",
                "test",
                "api",
                "shop",
                "vpn",
            ]
            for sub in common_subs:
                url = f"http://{sub}.{domain}"
                try:
                    if requests.get(url, timeout=2).status_code < 404:
                        result.append(f"Found: {url}")
                except:
                    pass
            return "\n".join(result) if result else "No subdomains found"
        except Exception as e:
            return f"Error: {str(e)}"

    def subnet_calc(self, ip_cidr):
        try:
            import ipaddress

            net = ipaddress.ip_network(ip_cidr, strict=False)
            return f"""Network: {net.network_address}
Netmask: {net.netmask}
Broadcast: {net.broadcast_address}
First IP: {net.network_address + 1}
Last IP: {net.broadcast_address - 1}
Total hosts: {net.num_addresses}
Subnet Class: {self._get_subnet_class(ip_cidr)}"""
        except Exception as e:
            return f"Error: {str(e)}"

    def _get_subnet_class(self, ip_cidr):
        try:
            ip = ip_cidr.split("/")[0]
            first = int(ip.split(".")[0])
            if first < 128:
                return "Class A (255.0.0.0)"
            elif first < 192:
                return "Class B (255.255.0.0)"
            elif first < 224:
                return "Class C (255.255.255.0)"
            elif first < 240:
                return "Class D (Multicast)"
            else:
                return "Class E (Reserved)"
        except:
            return "Unknown"

    def http_headers(self, url):
        try:
            import requests

            r = requests.head(url, timeout=10, allow_redirects=True)
            result = [
                f"Status: {r.status_code}",
                f"Server: {r.headers.get('Server', 'N/A')}",
            ]
            for k in [
                "Content-Type",
                "X-Powered-By",
                "Cache-Control",
                "Strict-Transport-Security",
                "X-Frame-Options",
            ]:
                v = r.headers.get(k)
                if v:
                    result.append(f"{k}: {v}")
            return "\n".join(result)
        except Exception as e:
            return f"Error: {str(e)}"

    def http_scan(self, url):
        try:
            import requests

            r = requests.get(url, timeout=10)
            result = [f"Status: {r.status_code}", f"Content-Length: {len(r.content)}"]
            result.append(f"Security Headers:")
            for h in [
                "X-Frame-Options",
                "X-XSS-Protection",
                "Content-Security-Policy",
                "Strict-Transport-Security",
            ]:
                v = r.headers.get(h)
                result.append(f"  {h}: {v if v else 'MISSING'}")
            return "\n".join(result)
        except Exception as e:
            return f"Error: {str(e)}"

    def website_scan(self, url):
        try:
            import requests

            result = []
            try:
                r = requests.get(url, timeout=10)
                result.append(f"[+] Status: {r.status_code}")
                result.append(f"[+] Server: {r.headers.get('Server', 'Unknown')}")
                result.append(
                    f"[+] Content-Type: {r.headers.get('Content-Type', 'Unknown')}"
                )
            except Exception as e:
                result.append(f"[-] Connection failed: {str(e)}")

            result.append("\n[+] Security Headers:")
            security_headers = [
                "X-Frame-Options",
                "X-Content-Type-Options",
                "X-XSS-Protection",
                "Strict-Transport-Security",
            ]
            for h in security_headers:
                result.append(f"    {h}: {'Present' if h in r.headers else 'MISSING'}")

            return "\n".join(result)
        except Exception as e:
            return f"Error: {str(e)}"

    def sql_injection_test(self, url):
        payloads = ["'", "' OR '1'='1", "' OR '1'='1' --", "' OR '1'='1' /*"]
        result = []
        for payload in payloads:
            try:
                import requests

                test_url = url + ("?" if "?" not in url else "&") + f"id={payload}"
                r = requests.get(test_url, timeout=5)
                if any(
                    x in r.text.lower()
                    for x in ["sql", "error", "mysql", "warning", "syntax"]
                ):
                    result.append(f"[!] Possible SQL Injection: {payload}")
                else:
                    result.append(f"[-] No injection detected: {payload}")
            except:
                pass
        return (
            "\n".join(result) if result else "No SQL injection vulnerabilities detected"
        )

    def xss_test(self, url):
        payloads = [
            "<script>alert('XSS')</script>",
            "<img src=x onerror=alert('XSS')>",
            "<svg onload=alert('XSS')>",
        ]
        result = []
        for payload in payloads:
            result.append(f"[*] XSS Payload: {payload}")
            result.append(f"    Test URL: {url}?q={payload}")
        return "\n".join(result)

    def ssl_check(self, host, port=443):
        try:
            import ssl

            ctx = ssl.create_default_context()
            with socket.create_connection((host, port)) as sock:
                with ctx.wrap_socket(sock, server_hostname=host) as ssock:
                    cert = ssock.getpeercert(binary_form=True)
                    import ssl

                    cert_dict = ssl._ssl._test_decode_cert(ssock.getpeercert())
                    return f"""SSL Certificate Info:
Host: {host}:{port}
Cipher: {ssock.cipher()[0]}
Protocol: {ssock.cipher()[1]}
Valid: {"Yes" if cert_dict.get("notAfter") else "Unknown"}"""
        except Exception as e:
            return f"Error: {str(e)}"

    def wifi_scan(self):
        try:
            result = []
            if self.os_type == "linux":
                result.append("[*] Scanning for WiFi networks...")
                try:
                    interfaces = subprocess.run(
                        ["iw", "dev"], capture_output=True, text=True
                    ).stdout
                    result.append(interfaces)
                except:
                    pass
                try:
                    nmcli = subprocess.run(
                        ["nmcli", "-t", "-f", "SSID,BSSID,SIGNAL", "dev", "wifi"],
                        capture_output=True,
                        text=True,
                    ).stdout
                    result.append("\n[+] WiFi Networks:")
                    result.append(nmcli)
                except:
                    pass
            elif self.os_type == "windows":
                result.append(
                    "[*] WiFi scan (run 'netsh wlan show networks mode=bssid' in cmd)"
                )
            return "\n".join(result) if result else "WiFi scanning not available"
        except Exception as e:
            return f"Error: {str(e)}"

    def bluetooth_scan(self):
        try:
            result = []
            if self.os_type == "linux":
                result.append("[*] Scanning for Bluetooth devices...")
                try:
                    scan = subprocess.run(
                        ["bluetoothctl", "scan", "on"],
                        capture_output=True,
                        text=True,
                        timeout=5,
                    )
                    result.append(
                        scan.stdout if scan.stdout else "Bluetooth scan initiated"
                    )
                except:
                    pass
                try:
                    devices = subprocess.run(
                        ["bluetoothctl", "devices"], capture_output=True, text=True
                    ).stdout
                    result.append(f"\n[+] Paired Devices:\n{devices}")
                except:
                    pass
            elif self.os_type == "windows":
                result.append("[*] Bluetooth scan (check Windows Bluetooth settings)")
            return "\n".join(result) if result else "Bluetooth scanning not available"
        except Exception as e:
            return f"Error: {str(e)}"

    def phone_info(self, ip_or_domain):
        try:
            result = []
            result.append(f"[+] Gathering info for: {ip_or_domain}")

            if not ip_or_domain.replace(".", "").replace("-", "").isdigit():
                ip = socket.gethostbyname(ip_or_domain)
            else:
                ip = ip_or_domain

            result.append(f"[+] IP: {ip}")

            geo = self.geoip_lookup(ip)
            result.append(f"\n[+] Geolocation:\n{geo}")

            result.append(f"\n[+] Reverse DNS: {self.reverse_dns(ip)}")

            result.append(f"\n[+] Port Scan (Top 20):")
            result.append(
                self.port_scan(
                    ip,
                    "1,20,21,22,23,25,53,80,110,143,443,445,993,995,3306,3389,5432,8080,8443",
                )
            )

            return "\n".join(result)
        except Exception as e:
            return f"Error: {str(e)}"

    def device_scan(self, network="192.168.1.0/24"):
        try:
            result = [f"[*] Scanning network: {network}"]
            import ipaddress

            net = ipaddress.ip_network(network, strict=False)
            active = []
            for ip in list(net.hosts())[:254]:
                ip_str = str(ip)
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(0.1)
                if sock.connect_ex((ip_str, 80)) == 0:
                    try:
                        hostname = socket.gethostbyaddr(ip_str)[0]
                    except:
                        hostname = "Unknown"
                    active.append(f"[+] {ip_str} - {hostname}")
                sock.close()
            return "\n".join(active) if active else "No active devices found"
        except Exception as e:
            return f"Error: {str(e)}"

    def mac_lookup(self, mac):
        try:
            mac = mac.upper().replace(":", "").replace("-", "")
            url = f"https://api.macvendors.com/{mac[:8]}"
            import requests

            return requests.get(url, timeout=5).text
        except Exception as e:
            return f"Error: {str(e)}"

    def hash_type(self, hash_str):
        length = len(hash_str)
        result = []
        if re.match(r"^[a-fA-F0-9]{32}$", hash_str):
            result.append("MD5")
        if re.match(r"^[a-fA-F0-9]{40}$", hash_str):
            result.append("SHA-1")
        if re.match(r"^[a-fA-F0-9]{64}$", hash_str):
            result.append("SHA-256")
        if re.match(r"^[a-fA-F0-9]{128}$", hash_str):
            result.append("SHA-512")
        if re.match(r"^\$[\w]+\$.+\$.+", hash_str):
            result.append("bcrypt")
        if re.match(r"^\$2[aby]?\$\d{2}\$.{53}", hash_str):
            result.append("bcrypt/blowfish")
        if re.match(r"^{SHA}", hash_str):
            result.append("SHA-1 (Base64)")
        if re.match(r"^\{SSHA\}", hash_str):
            result.append("Salted SHA-1")
        return ", ".join(result) if result else f"Unknown (length: {length})"

    def hash_identify(self, hash_str):
        info = {
            "Hash": hash_str[:50] + "..." if len(hash_str) > 50 else hash_str,
            "Length": len(hash_str),
            "Charset": "hex"
            if re.match(r"^[a-f0-9]+$", hash_str.lower())
            else "alphanumeric",
            "Possible Types": self.hash_type(hash_str),
        }
        return "\n".join([f"{k}: {v}" for k, v in info.items()])

    def password_strength(self, password):
        score = 0
        result = [f"Password: {'*' * len(password)}"]
        if len(password) >= 8:
            score += 1
        if len(password) >= 12:
            score += 1
        if re.search(r"[a-z]", password):
            score += 1
        if re.search(r"[A-Z]", password):
            score += 1
        if re.search(r"[0-9]", password):
            score += 1
        if re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
            score += 1
        result.append(f"Length: {len(password)}")
        result.append(f"Score: {score}/6")
        result.append(
            f"Strength: {'Weak' if score < 3 else 'Medium' if score < 5 else 'Strong'}"
        )
        return "\n".join(result)

    def base64_encode(self, text):
        import base64

        return base64.b64encode(text.encode()).decode()

    def base64_decode(self, text):
        import base64

        try:
            return base64.b64decode(text.encode()).decode()
        except:
            return "Invalid Base64"

    def hex_encode(self, text):
        return text.encode().hex()

    def hex_decode(self, hex_str):
        try:
            return bytes.fromhex(hex_str).decode()
        except:
            return "Invalid Hex"

    def url_encode(self, text):
        from urllib.parse import quote

        return quote(text)

    def url_decode(self, text):
        from urllib.parse import unquote

        return unquote(text)

    def html_encode(self, text):
        return (
            text.replace("&", "&amp;")
            .replace("<", "&lt;")
            .replace(">", "&gt;")
            .replace('"', "&quot;")
        )

    def html_decode(self, text):
        return (
            text.replace("&lt;", "<")
            .replace("&gt;", ">")
            .replace("&quot;", '"')
            .replace("&amp;", "&")
        )

    def whois_lookup(self, domain):
        try:
            result = subprocess.run(
                ["whois", domain], capture_output=True, text=True, timeout=10
            )
            return result.stdout[:3000] if result.stdout else result.stderr
        except FileNotFoundError:
            return "whois not installed. Install with: sudo apt install whois"
        except Exception as e:
            return f"Error: {str(e)}"

    def geoip_lookup(self, ip):
        try:
            import requests

            r = requests.get(f"http://ip-api.com/json/{ip}", timeout=5)
            data = r.json()
            if data["status"] == "success":
                return f"""IP: {data.get("query")}
Country: {data.get("country")} ({data.get("countryCode")})
Region: {data.get("regionName")}
City: {data.get("city")}
ISP: {data.get("isp")}
Org: {data.get("org")}
AS: {data.get("as")}
Coordinates: {data.get("lat")}, {data.get("lon")}
Timezone: {data.get("timezone")}"""
            return "IP not found"
        except Exception as e:
            return f"Error: {str(e)}"

    def vulners_scan(self, target):
        return f"""[*] Vulnerability Scan for {target}

Common CVE Check:
- Heartbleed (443): Test SSL/TLS
- Shellshock (CGI): Check headers
- POODLE: Check SSLv3
- Log4j: Check X-Api-Version headers

Run in terminal:
  nmap --script vuln {target}
  nikto -h {target}
  sqlmap -u {target}

Note: This is a basic check. For comprehensive scanning, use:
- Nikto (web vulnerabilities)
- Nmap with vulners script
- OpenVAS (full vulnerability scanner)
- Nessus (enterprise)"""

    def system_info(self):
        try:
            import platform
            import psutil

            return f"""[*] System Information

OS: {platform.system()} {platform.release()}
Python: {platform.python_version()}
Hostname: {platform.node()}
Architecture: {platform.machine()}
CPU: {platform.processor()}
RAM: {psutil.virtual_memory().total / (1024**3):.1f} GB
Public IP: {self.get_public_ip()}
MAC: {self.get_mac_address()}
Interfaces: {self.get_all_interfaces()}"""
        except Exception as e:
            return f"Error: {str(e)}"
