import os
import re
from django.core.management.base import BaseCommand
from django.conf import settings


LEGACY_RELATION_FIELDS = [
    'company_members_id',
    'company_projects_id',
    'company_addresses_id',
    'project_sales_document',
    'interaction_company_id',
]

LEGACY_DEPOSIT_FIELDS = [
    'sales_documents_deposit_id',
    'sales_documents_deposit_number',
    'sales_documents_deposit_date',
    'sales_documents_deposit_amount',
    'sales_documents_deposit_method',
]

LINE_FIELD_PREFIXES = [
    'sales_documents_index_line_',
    'sales_documents_quantity_',
    'sales_documents_description_',
    'sales_documents_description_free_',
    'sales_documents_price_',
    'sales_documents_discount_',
    'sales_documents_calculated_price_',
    'sales_documents_discounted_price_',
    'sales_documents_vat_',
]

IGNORE_DIR_PARTS = [
    os.sep + '__pycache__' + os.sep,
    os.sep + '.git' + os.sep,
    os.sep + 'venv' + os.sep,
    os.sep + 'env' + os.sep,
    os.sep + 'staticfiles' + os.sep,
]

IGNORE_FILE_ENDINGS = [
    '.pyc', '.pyo', '.sqlite3', '.db', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico',
    '.zip', '.tar', '.gz', '.pdf', '.xlsx', '.xls', '.docx', '.pptx',
]

TRANSITION_COMMANDS = [
    os.path.join('app_files', 'management', 'commands', 'sync_legacy_to_new_tables.py'),
    os.path.join('app_files', 'management', 'commands', 'check_refonte_sync.py'),
    os.path.join('app_files', 'management', 'commands', 'audit_legacy_references.py'),
]

SAFE_RELATION_SNIPPETS = [
    'legacy',
    '_rebuild_',
    '_sync_',
    '_legacy_',
    'compatibilité',
    'compatibility',
    'miroir',
    'maintenu',
    'reconstruit',
    'no fallback',
    'plus utilisé comme fallback',
    'source de vérité',
    'table_project_sales_documents',
    'models_project_sales_documents',
    'project_sales_documents_links',
    'project_sales_document_created_at',
    'id_project_sales_document',
    'updated_project_sales_documents_list',
    'div_update_record_project_sales_documents_timeline',
]

SAFE_DEPOSIT_SNIPPETS = [
    'models_sales_document_deposits',
    'table_sales_document_deposits',
    'sales_documents_deposit_created_at',
    'id_sales_document_deposit',
    '_rebuild_sales_documents_deposits_legacy_from_relations',
    '_sync_sales_documents_deposits_from_legacy',
    'compatibilité',
    'compatibility',
]


def relative_path(path, root):
    return os.path.relpath(path, root).replace(os.sep, '/')


def is_probably_binary(path):
    try:
        with open(path, 'rb') as f:
            chunk = f.read(2048)
        return b'\0' in chunk
    except Exception:
        return True


def should_skip_file(path, root, include_migrations, include_transition_commands):
    normalized = os.sep + os.path.relpath(path, root)
    for part in IGNORE_DIR_PARTS:
        if part in normalized:
            return True

    if any(path.endswith(ext) for ext in IGNORE_FILE_ENDINGS):
        return True

    rel = relative_path(path, root)

    if not include_migrations and '/migrations/' in '/' + rel:
        return True

    if not include_transition_commands:
        for command_path in TRANSITION_COMMANDS:
            if rel == command_path.replace(os.sep, '/'):
                return True

    return is_probably_binary(path)


def classify_line(rel_path, line):
    stripped = line.strip()

    if not stripped:
        return 'ignore_empty'

    if stripped.startswith('#') or stripped.startswith('//') or stripped.startswith('<!--'):
        return 'comment'

    if '/migrations/' in '/' + rel_path:
        return 'migration'

    if rel_path.endswith('models.py'):
        if 'class models_project_sales_documents' in line or 'models_project_sales_documents' in line:
            return 'new_model_name'
        if 'class models_sales_document_deposits' in line or 'models_sales_document_deposits' in line:
            return 'new_model_name'
        return 'model_field_still_present'

    if any(prefix in line for prefix in LINE_FIELD_PREFIXES):
        return 'line_fields_front_contract'

    if any(field in line for field in LEGACY_DEPOSIT_FIELDS):
        if any(snippet in line for snippet in SAFE_DEPOSIT_SNIPPETS):
            return 'safe_deposit_new_table_or_mirror'
        if rel_path.endswith('views.py') and ('doc.' in line or 'update_fields' in line or 'setattr' in line):
            return 'legacy_mirror_write'
        return 'active_deposit_reference'

    if any(field in line for field in LEGACY_RELATION_FIELDS):
        if any(snippet in line for snippet in SAFE_RELATION_SNIPPETS):
            return 'safe_relation_new_table_or_mirror'
        if rel_path.endswith('forms.py'):
            return 'hidden_legacy_form_field'
        if rel_path.endswith('.html'):
            return 'hidden_legacy_template_or_selection_var'
        if rel_path.endswith('.js'):
            return 'active_js_legacy_write'
        if rel_path.endswith('views.py'):
            if 'request.POST.get' in line:
                return 'active_view_reads_post_legacy'
            if 'objects.raw' in line or '__icontains' in line or 'RLIKE' in line or 'Q(' in line:
                return 'active_view_legacy_query'
            if '.save(update_fields' in line or '=' in line:
                return 'legacy_mirror_write'
        return 'active_relation_reference'

    return 'unknown'


class Command(BaseCommand):
    help = 'Audit intelligent des dernières références aux anciens champs legacy CRM.'

    def add_arguments(self, parser):
        parser.add_argument('--with-line-fields', action='store_true', help='Inclure aussi les champs legacy des 25 lignes document.')
        parser.add_argument('--include-migrations', action='store_true', help='Inclure les migrations Django.')
        parser.add_argument('--include-transition-commands', action='store_true', help='Inclure les commandes de transition/synchro.')
        parser.add_argument('--all', action='store_true', help='Afficher toutes les références, y compris celles considérées comme normales.')

    def handle(self, *args, **options):
        root = settings.BASE_DIR
        with_line_fields = options['with_line_fields']
        include_migrations = options['include_migrations']
        include_transition_commands = options['include_transition_commands']
        show_all = options['all']

        fields_to_find = list(LEGACY_RELATION_FIELDS) + list(LEGACY_DEPOSIT_FIELDS)
        if with_line_fields:
            fields_to_find += LINE_FIELD_PREFIXES

        results = []
        counts_by_category = {}

        for dirpath, dirnames, filenames in os.walk(root):
            for filename in filenames:
                path = os.path.join(dirpath, filename)
                if should_skip_file(path, root, include_migrations, include_transition_commands):
                    continue

                rel = relative_path(path, root)

                try:
                    with open(path, 'r', encoding='utf-8', errors='replace') as f:
                        lines = f.readlines()
                except Exception:
                    continue

                for index, line in enumerate(lines, start=1):
                    matched = [field for field in fields_to_find if field in line]
                    if not matched:
                        continue

                    category = classify_line(rel, line)
                    counts_by_category[category] = counts_by_category.get(category, 0) + 1

                    if not show_all:
                        if category in [
                            'comment',
                            'migration',
                            'model_field_still_present',
                            'new_model_name',
                            'safe_relation_new_table_or_mirror',
                            'safe_deposit_new_table_or_mirror',
                            'legacy_mirror_write',
                            'line_fields_front_contract',
                        ]:
                            continue

                    results.append({
                        'file': rel,
                        'line_number': index,
                        'fields': matched,
                        'category': category,
                        'line': line.rstrip('\n')[:240],
                    })

        self.stdout.write('Audit intelligent références legacy CRM')
        self.stdout.write('---------------------------------------')
        self.stdout.write(f'Dossier analysé : {root}')
        self.stdout.write('')

        self.stdout.write('Résumé par catégorie')
        self.stdout.write('--------------------')
        for category in sorted(counts_by_category):
            self.stdout.write(f'- {category}: {counts_by_category[category]}')
        self.stdout.write('')

        if results:
            self.stdout.write('Références à traiter en priorité')
            self.stdout.write('--------------------------------')
            current_file = None
            for item in results:
                if item['file'] != current_file:
                    current_file = item['file']
                    self.stdout.write('')
                    self.stdout.write(current_file)
                self.stdout.write(f"  L{item['line_number']} | {', '.join(item['fields'])} | {item['category']}")
                self.stdout.write(f"       {item['line']}")
        else:
            self.stdout.write('Aucune référence prioritaire trouvée avec les filtres actuels.')

        self.stdout.write('')
        self.stdout.write('Notes :')
        self.stdout.write('- Sans option, la commande masque les migrations, les miroirs legacy volontaires, les commentaires et les nouveaux noms de tables.')
        self.stdout.write('- --all affiche tout, y compris ce qui est considéré normal/temporaire.')
        self.stdout.write('- --with-line-fields inclut les 25 champs document, mais ils sont encore le contrat front HTML/JS actuel.')
        self.stdout.write('- Les catégories active_js_legacy_write, active_view_legacy_query, active_view_reads_post_legacy et hidden_legacy_form_field sont les plus utiles à nettoyer.')
