"""Reproduce aggregate national BODACC transfer-announcement counts.

Source: DILA, BODACC open data, Open Licence 2.0.
The public output contains aggregates only. Publication date is not move date.
Run: python3 analyse_bodacc.py --output report.json
"""
import argparse
import collections
import datetime
import hashlib
import json
import urllib.parse
import urllib.request

BASE = 'https://bodacc-datadila.opendatasoft.com/api/explore/v2.1/catalog/datasets/annonces-commerciales'
WHERE = "dateparution >= date'2025-01-01' and dateparution < date'2026-01-01' and publicationavis = 'A' and familleavis = 'immatriculation' and typeavis = 'annonce' and search(acte, 'transfert')"
FIELDS = 'id,dateparution,numerodepartement,departement_nom_officiel,region_nom_officiel,acte'
CATEGORIES = {
    "Immatriculation d'une personne morale suite à transfert de son siège social": 'registered_office',
    "Immatriculation d'une personne physique suite à transfert de l'établissement principal": 'main_establishment',
}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--output', default='resultats.json')
    args = parser.parse_args()
    params = {'where': WHERE, 'select': FIELDS, 'order_by': 'id'}
    url = BASE + '/exports/jsonl?' + urllib.parse.urlencode(params)
    seen = set()
    categories = collections.Counter()
    all_categories = collections.Counter()
    regions = collections.Counter()
    departments = collections.Counter()
    department_names = {}
    months = collections.Counter()
    counts = collections.Counter()
    digest = hashlib.sha256()
    with urllib.request.urlopen(url, timeout=60) as response:
        for line in response:
            digest.update(line)
            if not line.strip():
                continue
            row = json.loads(line)
            counts['candidate_rows'] += 1
            if row['id'] in seen:
                counts['duplicate_ids'] += 1
                continue
            seen.add(row['id'])
            act = json.loads(row['acte'] or '{}')
            category = act.get('immatriculation', {}).get('categorieImmatriculation', '')
            all_categories[category] += 1
            if category not in CATEGORIES:
                counts['excluded_other_category'] += 1
                continue
            counts['announcements'] += 1
            categories[CATEGORIES[category]] += 1
            region = row.get('region_nom_officiel') or 'Non renseignée'
            dep = row.get('numerodepartement') or 'Non renseigné'
            regions[region] += 1
            departments[dep] += 1
            department_names[dep] = row.get('departement_nom_officiel') or dep
            months[row['dateparution'][:7]] += 1
    assert sum(categories.values()) == counts['announcements']
    assert sum(regions.values()) == counts['announcements']
    assert sum(months.values()) == counts['announcements']
    assert sum(departments.values()) == counts['announcements']
    result = {
        'generated_at': datetime.datetime.now(datetime.timezone.utc).isoformat(),
        'producer': 'Direction de l’information légale et administrative (DILA)',
        'source_name': 'BODACC — Annonces commerciales',
        'source_url': url,
        'licence': 'Licence Ouverte / Open Licence 2.0',
        'period': {'start': '2025-01-01', 'end': '2025-12-31', 'basis': 'dateparution'},
        'unit': 'Avis initial distinct d’immatriculation après transfert, compté par id',
        'geography': 'France, selon le département et la région associés à l’annonce BODACC',
        'query': params,
        'export_sha256': digest.hexdigest(),
        'counts': dict(counts),
        'included_categories': dict(CATEGORIES),
        'source_categories': dict(all_categories),
        'by_category': dict(categories),
        'by_region': [{'region': k, 'announcements': v} for k,v in sorted(regions.items(), key=lambda item: (-item[1], item[0]))],
        'by_department': [{'department_code': k, 'department': department_names[k], 'announcements': v} for k,v in sorted(departments.items())],
        'by_month': [{'month': k, 'announcements': v} for k,v in sorted(months.items())],
        'limits': [
            'Le périmètre porte sur les immatriculations après transfert, notamment hors ressort. Il ne compte pas tous les transferts ni toutes les modifications d’adresse.',
            'Une annonce n’est ni une entreprise unique, ni un déménagement physique observé, ni un besoin immobilier confirmé.',
            'Le classement territorial reprend la géographie associée à l’annonce ; il ne mesure pas un solde d’entrées-sorties.',
            'Le siège peut être domicilié et ne pas correspondre au lieu d’exploitation.',
            'La date de publication ne donne ni la date du déménagement, ni une intention future.',
            'Les avis rectificatifs et d’annulation sont exclus du comptage, sans réconciliation rétroactive des avis initiaux.',
            'Les annonces publiques et leurs classifications peuvent être corrigées après l’extraction.',
        ],
    }
    with open(args.output, 'w', encoding='utf-8') as file:
        json.dump(result, file, ensure_ascii=False, indent=2)
        file.write('\n')
    print(json.dumps({'counts': result['counts'], 'by_category': result['by_category'], 'source_categories': result['source_categories'], 'regions': len(regions), 'departments': len(departments), 'export_sha256': result['export_sha256']}, ensure_ascii=False))

if __name__ == '__main__':
    main()
