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

import sys
import os
import json
import logging
import tempfile
import shutil
import subprocess
from datetime import datetime
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qs, urlparse

# Configuration du logging
log_dir = '/opt/splunk/var/log/splunk'
os.makedirs(log_dir, exist_ok=True)

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(os.path.join(log_dir, 'git_pusher.log')),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger('git_pusher')


class GitPusherRequestHandler(BaseHTTPRequestHandler):
    """Handler pour les requêtes HTTP"""
    
    def do_OPTIONS(self):
        """Traiter les requêtes OPTIONS (CORS preflight)"""
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()
    
    def do_POST(self):
        """Traiter les requêtes POST"""
        # Envoyer les headers CORS EN PREMIER
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.end_headers()
        
        try:
            logger.info(f"POST request to {self.path}")
            
            # Parser l'URL et les paramètres
            parsed_url = urlparse(self.path)
            query_params = parse_qs(parsed_url.query)
            
            logger.info(f"Query params keys: {list(query_params.keys())}")
            
            # Extraire les paramètres
            git_url = query_params.get('git_url', [''])[0]
            git_branch = query_params.get('git_branch', ['main'])[0]
            git_token = query_params.get('git_token', [''])[0]
            commit_message = query_params.get('commit_message', [''])[0]
            
            # Accepter soit 'apps' soit 'dashboards'
            apps_json = query_params.get('apps', query_params.get('dashboards', ['[]']))[0]
            user = query_params.get('user', ['unknown'])[0]
            
            logger.info(f"Parameters received: git_url={git_url}, branch={git_branch}, user={user}")
            logger.info(f"Raw apps_json: '{apps_json}'")
            
            # Parser les apps
            try:
                # parse_qs décode déjà, mais au cas où
                if isinstance(apps_json, str):
                    apps = json.loads(apps_json)
                else:
                    apps = apps_json
            except (json.JSONDecodeError, TypeError) as e:
                logger.error(f"JSON parse error: {e} - trying to parse: {apps_json}")
                apps = []
            
            logger.info(f"Parsed apps: {len(apps)} items - {apps}")
            
            # Valider
            if not git_url or not git_token or not commit_message or not apps:
                logger.warning(f"Validation failed: git_url={bool(git_url)}, git_token={bool(git_token)}, commit_message={bool(commit_message)}, apps={len(apps)}")
                response = {
                    'status': 'error',
                    'message': 'Missing required parameters'
                }
                self.wfile.write(json.dumps(response).encode())
                return
            
            # Créer un répertoire temporaire
            temp_dir = tempfile.mkdtemp(prefix='splunk_git_')
            logger.info(f"Created temp directory: {temp_dir}")
            
            try:
                # Préparer l'URL Git avec le token
                git_url_with_token = self.prepare_git_url(git_url, git_token)
                
                logger.info(f"Git URL prepared (token inserted)")
                logger.debug(f"Git URL with token: {git_url_with_token}")
                logger.info("Cloning repository...")
                self.clone_repository(temp_dir, git_url_with_token, git_branch)
                
                # Récupérer TOUTES les applications (dossiers complets)
                logger.info("Fetching applications from Splunk...")
                dashboard_contents = self.fetch_apps_directories(apps)
                
                # Créer le dossier apps
                apps_dir = os.path.join(temp_dir, 'apps')
                os.makedirs(apps_dir, exist_ok=True)
                
                # Copier les applications
                logger.info("Copying applications to repository...")
                for app_data in dashboard_contents:
                    app_name = app_data['name']
                    app_path = app_data['path']
                    dest_path = os.path.join(apps_dir, app_name)
                    
                    if os.path.exists(app_path):
                        logger.info(f"Copying app {app_name} from {app_path}")
                        # Supprimer le dossier s'il existe déjà
                        if os.path.exists(dest_path):
                            logger.info(f"Removing existing app directory: {dest_path}")
                            shutil.rmtree(dest_path)
                        # Copier le dossier
                        shutil.copytree(app_path, dest_path)
                        logger.info(f"Copied app: {app_name}")
                    else:
                        logger.warning(f"App path not found: {app_path}")
                
                # Configurer git
                logger.info("Configuring git...")
                subprocess.run(['git', 'config', 'user.email', 'splunk@splunk.local'], 
                             cwd=temp_dir, capture_output=True)
                subprocess.run(['git', 'config', 'user.name', 'Splunk Git Pusher'], 
                             cwd=temp_dir, capture_output=True)
                
                # Commit et push
                logger.info("Adding files...")
                subprocess.run(['git', 'add', '-A'], cwd=temp_dir, capture_output=True)
                
                full_message = f"{commit_message}\n\nPushed by: {user}\nTimestamp: {datetime.now().isoformat()}"
                logger.info("Committing...")
                result = subprocess.run(['git', 'commit', '-m', full_message], 
                                      cwd=temp_dir, capture_output=True, text=True)
                
                if result.returncode != 0:
                    logger.warning(f"Commit may have failed or had no changes: {result.stderr}")
                
                logger.info("Pushing...")
                result = subprocess.run(['git', 'push', 'origin', git_branch], 
                                      cwd=temp_dir, capture_output=True, text=True, timeout=60)
                
                if result.returncode != 0:
                    raise Exception(f"Push failed: {result.stderr}")
                
                logger.info("Push successful!")
                response = {
                    'status': 'success',
                    'message': f'Successfully pushed {len(dashboard_contents)} dashboards from {len(apps)} application(s) to Git',
                    'dashboards_pushed': len(dashboard_contents)
                }
                self.wfile.write(json.dumps(response).encode())
            
            finally:
                logger.info(f"Cleaning up {temp_dir}")
                shutil.rmtree(temp_dir, ignore_errors=True)
        
        except Exception as e:
            logger.error(f"Error: {str(e)}", exc_info=True)
            response = {
                'status': 'error',
                'message': f'Error: {str(e)}'
            }
            self.wfile.write(json.dumps(response).encode())
    
    def log_message(self, format, *args):
        """Éviter les logs HTTP par défaut"""
        logger.debug(format % args)
    
    @staticmethod
    def prepare_git_url(git_url, token):
        """Préparer l'URL Git avec le token inséré"""
        logger.info(f"Preparing git URL with token")
        
        # Si l'URL contient déjà un token (format: https://user:token@host/repo)
        # on le remplace
        if '@' in git_url:
            # Extraire la partie sans le token
            protocol = git_url.split('://')[0]
            rest = git_url.split('://', 1)[1]
            host_and_path = rest.split('@', 1)[1] if '@' in rest else rest
            return f"{protocol}://{token}@{host_and_path}"
        
        # Si l'URL est juste https://host/repo (sans credentials)
        if git_url.startswith('https://') or git_url.startswith('http://'):
            protocol = git_url.split('://')[0]
            host_and_path = git_url.split('://', 1)[1]
            # Insérer le token au format user:token@host ou juste token@host
            return f"{protocol}://{token}@{host_and_path}"
        
        return git_url
    
    @staticmethod
    def clone_repository(dest_dir, git_url, branch):
        """Cloner le repository"""
        try:
            cmd = ['git', 'clone', '--depth', '1', '--branch', branch, git_url, dest_dir]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
            
            if result.returncode != 0:
                raise Exception(f"Clone failed: {result.stderr}")
            
            logger.info("Repository cloned successfully")
        except subprocess.TimeoutExpired:
            raise Exception("Git clone operation timed out")
        except FileNotFoundError:
            raise Exception("Git is not installed on this system")
    
    @staticmethod
    def fetch_apps_directories(apps):
        """Récupérer les dossiers complets des applications"""
        logger.info(f"Fetching directories for {len(apps)} applications")
        
        splunk_home = '/opt/splunk'
        apps_base_path = os.path.join(splunk_home, 'etc', 'apps')
        
        app_directories = []
        
        for app in apps:
            app_id = app.get('id') or app.get('app_id')
            app_path = os.path.join(apps_base_path, app_id)
            
            logger.info(f"Checking app directory: {app_path}")
            
            if os.path.isdir(app_path):
                app_directories.append({
                    'name': app_id,
                    'path': app_path,
                    'size': sum(os.path.getsize(os.path.join(dirpath, filename)) 
                               for dirpath, dirnames, filenames in os.walk(app_path) 
                               for filename in filenames)
                })
                logger.info(f"Found app: {app_id} at {app_path}")
            else:
                logger.warning(f"App directory not found: {app_path}")
        
        logger.info(f"Successfully found {len(app_directories)} application directories")
        return app_directories
        """Récupérer TOUS les dashboards de chaque application"""
        logger.info(f"Fetching dashboards from {len(apps)} applications")
        
        import urllib.request
        import urllib.error
        import ssl
        import base64
        
        # Ignorer les certificats SSL auto-signés
        ssl_context = ssl.create_default_context()
        ssl_context.check_hostname = False
        ssl_context.verify_mode = ssl.CERT_NONE
        
        dashboard_contents = []
        
        # Lire le fichier de configuration Splunk pour obtenir les credentials
        # Ou utiliser des credentials par défaut
        splunk_username = os.environ.get('SPLUNK_USERNAME', 'admin')
        splunk_password = os.environ.get('SPLUNK_PASSWORD', 'changeme')
        
        # Créer l'authentification Basic
        credentials = base64.b64encode(f"{splunk_username}:{splunk_password}".encode()).decode()
        
        for app in apps:
            app_id = app.get('id') or app.get('app_id')
            logger.info(f"Fetching all dashboards from app: {app_id}")
            
            try:
                # Récupérer la liste de TOUS les dashboards de cette app
                api_url = f"https://127.0.0.1:8089/servicesNS/-/{app_id}/data/ui/views?output_mode=json&count=0"
                
                logger.debug(f"API URL: {api_url}")
                
                req = urllib.request.Request(api_url)
                req.add_header('Authorization', f'Basic {credentials}')
                
                with urllib.request.urlopen(req, timeout=15, context=ssl_context) as response:
                    api_data = json.loads(response.read().decode('utf-8'))
                    
                    if 'entry' in api_data and len(api_data['entry']) > 0:
                        for entry in api_data['entry']:
                            try:
                                dashboard_id = entry.get('name')
                                content = entry.get('content', {})
                                
                                # eai:data contient le XML complet du dashboard
                                dashboard_xml = content.get('eai:data', '')
                                
                                if dashboard_xml:
                                    dashboard_contents.append({
                                        'id': f"{app_id}_{dashboard_id}",
                                        'app': app_id,
                                        'content': dashboard_xml,
                                        'name': dashboard_id
                                    })
                                    logger.debug(f"Fetched: {dashboard_id} from {app_id}")
                            except Exception as e:
                                logger.error(f"Error processing dashboard entry: {str(e)}")
                        
                        logger.info(f"Found {len([d for d in dashboard_contents if d['app'] == app_id])} dashboards in {app_id}")
                    else:
                        logger.warning(f"No dashboards found in app {app_id}")
            
            except urllib.error.HTTPError as e:
                logger.error(f"HTTP {e.code} when fetching app {app_id}: {e.reason}")
            except urllib.error.URLError as e:
                logger.error(f"Cannot reach Splunk API for app {app_id}: {e.reason}")
            except Exception as e:
                logger.error(f"Error fetching dashboards from {app_id}: {str(e)}")
        
        logger.info(f"Successfully fetched {len(dashboard_contents)} dashboards total")
        return dashboard_contents


def start_server(port=9999):
    """Démarrer le serveur HTTP"""
    server = HTTPServer(('0.0.0.0', port), GitPusherRequestHandler)
    logger.info(f"Git Pusher server listening on 0.0.0.0:{port} (HTTP)")
    server.serve_forever()


if __name__ == '__main__':
    # Démarrer le serveur en background
    port = 9999
    logger.info(f"Starting Git Pusher on port {port}")
    start_server(port)