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


import os
import re
import sys
import time
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from django.urls import get_resolver, URLPattern, URLResolver
from django.apps import apps
from django.db import transaction

# Import des modèles (Ajustez 'analyzer' selon le nom de votre app)
from analyzer.models import ScanReport, DetectedUrl, AnalyzedTemplate, TemplateTagUsage, ViewTemplateUsage

class Command(BaseCommand):
    help = 'Audit complet : URLs, Templates, Vues et stockage SQL.'

    def add_arguments(self, parser):
        parser.add_argument('--app', type=str, help='Cibler une application')
        parser.add_argument('--path', type=str, help='Cibler un dossier')

    def print_progress(self, iteration, total, prefix='', suffix='', decimals=1, length=30, fill='█'):
        if total == 0: return
        percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
        filled_length = int(length * iteration // total)
        bar = fill * filled_length + '-' * (length - filled_length)
        sys.stdout.write(f'\r{prefix} |{self.style.HTTP_INFO(bar)}| {percent}% {suffix}')
        sys.stdout.flush()

    @transaction.atomic
    def handle(self, *args, **options):
        start_time = time.time()
        self.stdout.write(self.style.MIGRATE_HEADING("\n📊 DJANGO AUDITOR V4.0 (SQL EDITION)"))
        
        # 1. CRÉATION DU RAPPORT
        scope = options['app'] or options['path'] or "FULL PROJECT"
        report = ScanReport.objects.create(scope_path=scope)
        self.stdout.write(f"📝 Rapport ID #{report.id} initialisé.")

        # --- PHASE 2 : INDEXATION DES URLs ---
        self.stdout.write("1️⃣  Indexation des routes urls.py...")
        valid_url_names = set()
        
        def extract(patterns, ns=None):
            for p in patterns:
                if isinstance(p, URLPattern) and p.name:
                    full_name = f"{ns}:{p.name}" if ns else p.name
                    pattern_desc = str(p.pattern)
                    
                    # Sauvegarde en base
                    DetectedUrl.objects.create(
                        scan=report,
                        name=full_name,
                        pattern=pattern_desc,
                        namespace=ns
                    )
                    valid_url_names.add(full_name)
                    
                elif isinstance(p, URLResolver):
                    new_ns = f"{ns}:{p.namespace}" if ns and p.namespace else (p.namespace or ns)
                    extract(p.url_patterns, new_ns)
        
        extract(get_resolver().url_patterns)
        report.total_urls_found = len(valid_url_names)
        self.stdout.write(self.style.SUCCESS(f"   -> {len(valid_url_names)} routes archivées."))

        # --- PHASE 3 : DÉTECTION DES FICHIERS ---
        target_dirs = set()
        if options['app']:
            target_dirs.add(apps.get_app_config(options['app']).path)
        elif options['path']:
            target_dirs.add(options['path'])
        else:
            base = str(settings.BASE_DIR)
            for tpl in settings.TEMPLATES:
                for d in tpl.get('DIRS', []): target_dirs.add(str(d))
            for conf in apps.get_app_configs():
                if conf.path.startswith(base) and 'site-packages' not in conf.path:
                    target_dirs.add(conf.path)

        tpl_files = []
        py_files = []
        
        for d in target_dirs:
            for root, dirs, files in os.walk(d):
                dirs[:] = [d for d in dirs if d not in ['__pycache__', 'migrations', 'venv', '.git', 'node_modules']]
                for f in files:
                    if f.endswith('.html'): tpl_files.append(os.path.join(root, f))
                    if f.endswith('.py'): py_files.append(os.path.join(root, f))

        # --- PHASE 4 : SCAN TEMPLATES (Cross-Reference) ---
        self.stdout.write(f"2️⃣  Analyse des Templates ({len(tpl_files)} fichiers)...")
        url_regex = re.compile(r"{%\s*url\s+(?:'([^']+)'|\"([^\"]+)\")")
        
        for i, fpath in enumerate(tpl_files):
            rel_path = os.path.relpath(fpath, settings.BASE_DIR)
            self.print_progress(i, len(tpl_files), prefix='Scan Tpl:', suffix=rel_path[:20])
            
            # Création objet Template en BDD
            tpl_obj = AnalyzedTemplate.objects.create(scan=report, file_path=rel_path)
            link_count = 0
            
            try:
                with open(fpath, 'r', encoding='utf-8') as f:
                    for l_idx, line in enumerate(f):
                        for m in url_regex.findall(line):
                            u_name = m[0] or m[1]
                            # Ignorer variables dynamiques
                            if " " in u_name or "{{" in u_name: continue
                            
                            is_valid = u_name in valid_url_names
                            err_msg = None if is_valid else "NoReverseMatch: URL introuvable"
                            
                            if not is_valid:
                                report.total_errors += 1
                                # Affichage console erreur
                                sys.stdout.write('\r' + ' '*80 + '\r')
                                self.stdout.write(self.style.ERROR(f"   ❌ {rel_path}:{l_idx+1} > {u_name}"))

                            # Sauvegarde du Tag
                            TemplateTagUsage.objects.create(
                                template=tpl_obj,
                                line_number=l_idx + 1,
                                url_name_used=u_name,
                                is_valid=is_valid,
                                error_message=err_msg
                            )
                            link_count += 1
            except Exception: pass
            
            tpl_obj.total_links = link_count
            tpl_obj.save()

        # --- PHASE 5 : SCAN VUES (Qui appelle quel template ?) ---
        self.stdout.write(f"\n3️⃣  Analyse des Vues Python ({len(py_files)} fichiers)...")
        # Regex pour trouver: template_name = 'xxx.html' OU render(request, 'xxx.html')
        view_regex = re.compile(r"(?:template_name\s*=\s*|render\s*\(\s*request\s*,\s*)(?:'([^']+)'|\"([^\"]+)\")")
        
        for i, fpath in enumerate(py_files):
            rel_path = os.path.relpath(fpath, settings.BASE_DIR)
            self.print_progress(i, len(py_files), prefix='Scan Py:', suffix=rel_path[:20])
            
            try:
                with open(fpath, 'r', encoding='utf-8') as f:
                    for l_idx, line in enumerate(f):
                        for m in view_regex.findall(line):
                            tpl_name = m[0] or m[1]
                            if tpl_name.endswith('.html'):
                                ViewTemplateUsage.objects.create(
                                    scan=report,
                                    view_file=rel_path,
                                    view_line=l_idx + 1,
                                    template_name=tpl_name
                                )
            except: pass

        # --- FINALISATION ---
        report.duration_seconds = time.time() - start_time
        report.total_templates_scanned = len(tpl_files)
        report.total_views_scanned = len(py_files)
        report.save()
        
        print("\n" + "="*40)
        self.stdout.write(self.style.SUCCESS(f"✅ Audit terminé en {report.duration_seconds:.2f}s"))
        self.stdout.write(f"📊 Données sauvegardées dans le Scan #{report.id}")
        self.stdout.write(f"   - URLs indexées : {report.total_urls_found}")
        self.stdout.write(f"   - Templates liés : {report.total_templates_scanned}")
        self.stdout.write(f"   - Erreurs détectées : {report.total_errors}")