import os
import sys
import psutil
import random
from flask import Flask, render_template, jsonify, request
from flask_cors import CORS

# Attempt to load Kharma internal modules depending on execution context
try:
    from kharma.scanner import NetworkScanner
    from kharma.geoip import GeoIPResolver
    from kharma.threat import ThreatIntelligence
    from kharma.vt_engine import VTEngine
    from kharma.community import CommunityIntel
    from kharma.dpi import DPIEngine
    from kharma.shield import ShieldManager
    from kharma.guardian import GuardianBot
    from kharma.forensics import ForensicsDB
except ImportError:
    from scanner import NetworkScanner
    from geoip import GeoIPResolver
    from threat import ThreatIntelligence
    from vt_engine import VTEngine
    from community import CommunityIntel
    from dpi import DPIEngine
    from shield import ShieldManager
    from guardian import GuardianBot
    from forensics import ForensicsDB

class KharmaWebServer:
    def __init__(self, host="127.0.0.1", port=8085):
        self.host = host
        self.port = port
        
        # Determine the correct templates folder path regardless of pip vs source install
        if getattr(sys, 'frozen', False):
            # PyInstaller context
            template_dir = os.path.join(sys._MEIPASS, 'templates')
        else:
            # Standard package context
            template_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
            
        self.app = Flask(__name__, template_folder=template_dir, static_folder=template_dir)
        CORS(self.app)  # Allow frontend requests
        self._setup_engines()
        self._setup_routes()

    def _setup_engines(self):
        """Initialize all the core data gathering engines."""
        self.scanner = NetworkScanner()
        self.geoip = GeoIPResolver()
        self.intel = ThreatIntelligence()
        self.vt_engine = VTEngine()
        self.community = CommunityIntel()
        self.dpi = DPIEngine()
        self.dpi.start() # Start background sniffing
        self.shield = ShieldManager()
        self.guardian = GuardianBot()
        self.forensics = ForensicsDB()

    def _setup_routes(self):
        @self.app.route('/')
        def index():
            """Serve the main Kharma Dashboard UI."""
            from flask import make_response
            resp = make_response(render_template('index.html'))
            resp.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
            resp.headers['Pragma'] = 'no-cache'
            return resp

        @self.app.route('/api/radar', methods=['GET'])
        def get_live_radar():
            """
            Fetches all active network connections and runs them through the 
            Threat Intel, GeoIP, and VirusTotal engines to build a complete JSON response.
            """
            try:
                self.scanner.scan()
                active_connections = self.scanner.get_active_connections()
                radar_data = []
                blocked_ips = self.shield.list_blocked()

                for conn in active_connections:
                    remote_ip = conn['remote_ip']
                    
                    # 1. GeoIP Lookup
                    location = "[LOCAL]"
                    country_code = "LOCAL"
                    lat, lon = None, None
                    if remote_ip and not remote_ip.startswith(('127.', '192.168.', '10.')):
                        lat_lon = self.geoip.resolve(remote_ip)
                        if isinstance(lat_lon, tuple):
                            if len(lat_lon) >= 3:
                                lat, lon, location_str = lat_lon[0], lat_lon[1], lat_lon[2]
                                location = location_str
                                country_code = location_str.split(',')[-1].strip() if ',' in location_str else "N/A"
                            else:
                                location = "Tuple Error"
                                country_code = "N/A"
                        else:
                            location = str(lat_lon) if lat_lon else "[UNKNOWN]"
                            country_code = "N/A"

                    # 2. Threat Intel (Malware Detection)
                    is_malware = False
                    status_text = conn['status']
                    if remote_ip and self.intel.check_ip(remote_ip):
                        is_malware = True
                        status_text = "BREACHED"

                    # 3. VirusTotal EDR Scoring
                    vt_malicious = -1
                    vt_total = -1
                    if self.vt_engine.api_key and conn.get('exe'):
                        file_hash = self.vt_engine.get_file_hash(conn['exe'])
                        if file_hash:
                            vt_m, vt_t = self.vt_engine.check_hash(file_hash)
                            if vt_m is not None:
                                vt_malicious = vt_m
                                vt_total = vt_t
                                if vt_malicious > 0:
                                    is_malware = True
                                    status_text = "BREACHED"

                    # 4. Community Reputation
                    is_community_flagged = self.community.is_flagged(remote_ip)
                    community_detail = self.community.get_details(remote_ip) if is_community_flagged else None
                    if is_community_flagged:
                        status_text = "SUSPICIOUS"

                    # Assemble JSON Row
                    radar_data.append({
                        "process_name": conn.get('name', 'Unknown'),
                        "pid": conn.get('pid', 'N/A'),
                        "exe": conn.get('exe', ''),
                        "local_address": f"{conn.get('local_ip')}:{conn.get('local_port')}",
                        "remote_address": f"{remote_ip}:{conn.get('remote_port')}",
                        "remote_ip": remote_ip, # Send raw IP for reporting
                        "location": location,
                        "country_code": country_code,
                        "lat": lat if lat is not None else (random.uniform(-40, 40) if country_code != 'LOCAL' else None),
                        "lon": lon if lon is not None else (random.uniform(-40, 40) if country_code != 'LOCAL' else None),
                        "status": status_text,
                        "is_malware": is_malware,
                        "is_community_flagged": is_community_flagged,
                        "community_reports": community_detail['reports'] if is_community_flagged else 0,
                        "is_shielded": remote_ip in blocked_ips,
                        "vt_malicious": vt_malicious,
                        "vt_total": vt_total
                    })

                    # 5. Auto-Shielding + Guardian + Forensics
                    if remote_ip not in blocked_ips:
                        risk_score = 0
                        if is_community_flagged and community_detail['reports'] >= 3: risk_score += 10
                        if vt_malicious > 5: risk_score += 10
                        if is_malware: risk_score += 10
                        
                        if risk_score >= 10:
                            print(f"[SHIELD] AUTO-BLOCKING HIGH RISK IP: {remote_ip} (Score: {risk_score})")
                            self.shield.block_ip(remote_ip)
                            blocked_ips.append(remote_ip)
                            self.guardian.alert_blocked(remote_ip, reason=f"Auto-Shield (Score: {risk_score})")
                            self.forensics.log(
                                event_type="BLOCKED",
                                ip=remote_ip,
                                process=conn.get('name', 'Unknown'),
                                location=location,
                                detail=f"Risk Score: {risk_score}",
                                severity="high"
                            )

                    # 6. Guardian + Forensics Threat Logging
                    if is_malware:
                        self.guardian.alert_threat(remote_ip, conn.get('name', 'Unknown'))
                        self.forensics.log(
                            event_type="THREAT",
                            ip=remote_ip,
                            process=conn.get('name', 'Unknown'),
                            location=location,
                            detail=f"VT:{vt_malicious}/{vt_total}" if vt_malicious >= 0 else "Threat Intel Match",
                            severity="critical"
                        )

                    # 7. Community Flag Logging
                    if is_community_flagged and community_detail['reports'] >= 3:
                        self.forensics.log(
                            event_type="COMMUNITY_FLAG",
                            ip=remote_ip,
                            process=conn.get('name', 'Unknown'),
                            location=location,
                            detail=f"{community_detail['reports']} community reports",
                            severity="medium"
                        )

                return jsonify({"status": "success", "data": radar_data}), 200

            except Exception as e:
                import traceback
                return jsonify({"status": "error", "message": str(e), "trace": traceback.format_exc()}), 500

        @self.app.route('/api/kill/<int:pid>', methods=['DELETE'])
        def kill_process(pid):
            """API Endpoint to instantly terminate a process via the Web UI."""
            try:
                p = psutil.Process(pid)
                p_name = p.name()
                p.terminate()
                p.wait(timeout=3)
                return jsonify({"status": "success", "message": f"Terminated {p_name} (PID: {pid})"}), 200
            except psutil.NoSuchProcess:
                return jsonify({"status": "error", "message": f"PID {pid} not found."}), 404
            except psutil.AccessDenied:
                return jsonify({"status": "error", "message": f"Access Denied. Run Kharma as Admin/Root to kill PID {pid}."}), 403
            except Exception as e:
                return jsonify({"status": "error", "message": f"Failed to kill process: {e}"}), 500

        @self.app.route('/api/report', methods=['POST'])
        def report_to_community():
            """API Endpoint to report a malicious IP to the decentralized Kharma community."""
            try:
                data = request.get_json()
                remote_ip = data.get('ip')
                reason = data.get('reason', 'Manual Flag')
                
                if not remote_ip:
                    return jsonify({"status": "error", "message": "Missing IP address."}), 400
                    
                success = self.community.report_ip(remote_ip, reason)
                if success:
                    return jsonify({"status": "success", "message": f"IP {remote_ip} has been reported to the community."}), 200
                else:
                    return jsonify({"status": "error", "message": "Failed to report IP."}), 500
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/shield', methods=['GET', 'POST', 'DELETE'])
        def manage_shield():
            """API Endpoint for manual firewall shield management."""
            try:
                if request.method == 'GET':
                    blocked = self.shield.list_blocked()
                    return jsonify({"status": "success", "data": blocked}), 200
                
                data = request.get_json()
                ip = data.get('ip')
                if not ip: return jsonify({"status": "error", "message": "No IP specified"}), 400

                if request.method == 'POST':
                    success = self.shield.block_ip(ip)
                    message = f"IP {ip} is now SHIELDED." if success else "Failed to block IP. Check admin rights."
                    return jsonify({"status": "success" if success else "error", "message": message}), 200 if success else 500
                
                if request.method == 'DELETE':
                    success = self.shield.unblock_ip(ip)
                    message = f"IP {ip} has been UNBLOCK." if success else "Failed to unblock IP."
                    return jsonify({"status": "success" if success else "error", "message": message}), 200 if success else 500

            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/packets', methods=['GET'])
        def get_packets():
            """API Endpoint for the Live Packet Telemetry stream."""
            try:
                packets = self.dpi.get_packets()
                return jsonify({"status": "success", "data": packets}), 200
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/settings', methods=['GET', 'POST'])
        def manage_settings():
            """API Endpoint to get/update Guardian Bot configuration."""
            try:
                if request.method == 'GET':
                    config = self.guardian.get_config()
                    return jsonify({"status": "success", "data": config}), 200
                
                data = request.get_json()
                self.guardian.save_config(data)
                return jsonify({"status": "success", "message": "Settings saved. Guardian Bot activated."}), 200
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/settings/test', methods=['POST'])
        def test_guardian():
            """Sends a test alert to verify Guardian Bot connectivity."""
            try:
                sent_to = self.guardian.send_test_alert()
                if sent_to:
                    return jsonify({"status": "success", "message": f"✅ Test alert sent to: {', '.join(sent_to)}"}), 200
                else:
                    return jsonify({"status": "error", "message": "No channels configured. Save your settings first."}), 400
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/history', methods=['GET', 'DELETE'])
        def manage_history():
            """API Endpoint to retrieve or clear security event history."""
            try:
                if request.method == 'GET':
                    event_type = request.args.get('type', None)
                    events = self.forensics.get_events(limit=200, event_type=event_type)
                    stats = self.forensics.get_stats()
                    return jsonify({"status": "success", "data": events, "stats": stats}), 200
                if request.method == 'DELETE':
                    self.forensics.clear()
                    return jsonify({"status": "success", "message": "History cleared."}), 200
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/history/export', methods=['GET'])
        def export_history():
            """Exports the full history as CSV or JSON."""
            from flask import Response
            fmt = request.args.get('format', 'json')
            try:
                if fmt == 'csv':
                    data = self.forensics.export_csv()
                    return Response(data, mimetype='text/csv',
                                    headers={"Content-Disposition": "attachment;filename=kharma_history.csv"})
                else:
                    data = self.forensics.export_json()
                    return Response(data, mimetype='application/json',
                                    headers={"Content-Disposition": "attachment;filename=kharma_history.json"})
            except Exception as e:
                return jsonify({"status": "error", "message": str(e)}), 500

        @self.app.route('/api/history/demo', methods=['POST'])
        def add_demo_events():
            """Inserts realistic demo events for UI demonstration purposes."""
            demo_events = [
                {"type": "THREAT",         "ip": "185.220.101.47", "process": "chrome.exe",    "location": "Moscow, RU",      "detail": "Known Tor Exit Node",            "severity": "critical"},
                {"type": "BLOCKED",        "ip": "194.165.16.117", "process": "edge.exe",      "location": "Tehran, IR",      "detail": "Risk Score: 30",                 "severity": "high"},
                {"type": "COMMUNITY_FLAG", "ip": "45.142.212.100", "process": "firefox.exe",   "location": "Amsterdam, NL",   "detail": "7 community reports",            "severity": "medium"},
                {"type": "THREAT",         "ip": "198.54.117.200", "process": "python.exe",    "location": "New York, US",    "detail": "VT:15/72",                       "severity": "critical"},
                {"type": "BLOCKED",        "ip": "91.108.4.50",    "process": "wsmprovhost.exe","location": "Singapore, SG",  "detail": "Risk Score: 20",                 "severity": "high"},
                {"type": "COMMUNITY_FLAG", "ip": "5.188.206.14",   "process": "svchost.exe",   "location": "Saint Petersburg, RU", "detail": "4 community reports",       "severity": "medium"},
                {"type": "THREAT",         "ip": "31.13.92.36",    "process": "chrome.exe",    "location": "Dublin, IE",      "detail": "Threat Intel Match",             "severity": "critical"},
            ]
            import time
            for ev in demo_events:
                self.forensics.log(
                    event_type=ev["type"], ip=ev["ip"],
                    process=ev["process"], location=ev["location"],
                    detail=ev["detail"], severity=ev["severity"]
                )
            return jsonify({"status": "success", "message": f"✅ Added {len(demo_events)} demo events to history."}), 200

    def start(self):
        """Start the Flask internal server. This is a blocking call."""
        # Disable Flask startup banner for stealth
        import logging
        log = logging.getLogger('werkzeug')
        log.setLevel(logging.ERROR)
        
        self.app.run(host=self.host, port=self.port, debug=False)

if __name__ == '__main__':
    server = KharmaWebServer()
    server.start()
