#!/usr/bin/env python3
"""
Mescla múltiplos arquivos GeoJSON em um único arquivo FeatureCollection.

Este script é útil quando você criou múltiplas camadas no QGIS com diferentes
tipos de geometria (Point, LineString, Polygon) e precisa combiná-las em um
único arquivo GeoJSON para submissão.

Uso básico:
    python mesclar_geojson.py arquivo1.geojson arquivo2.geojson -o saida.geojson

Exemplo real:
    python mesclar_geojson.py \\
        L13_points.geojson \\
        L13_lines.geojson \\
        L13_polygons.geojson \\
        -o L13_conservacao_2026_R0.geojson

O script:
- Combina todas as features dos arquivos de entrada
- Verifica IDs duplicados e emite avisos
- Adiciona CRS EPSG:4674 (SIRGAS 2000)
- Adiciona metadata com schema_version e data_geracao
- Preserva todas as propriedades das features originais

Requisitos:
    Python 3.6+
    Bibliotecas padrão (json, sys, argparse, datetime)
"""

import json
import sys
import os
import argparse
from datetime import datetime, timezone
from collections import Counter

def validar_geojson_entrada(arquivo):
    """Valida se o arquivo é um GeoJSON válido.

    Args:
        arquivo (str): Caminho para o arquivo GeoJSON.

    Returns:
        dict ou None: Dados do GeoJSON se válido, None caso contrário.
    """
    try:
        with open(arquivo, 'r', encoding='utf-8') as f:
            data = json.load(f)
    except FileNotFoundError:
        print(f"❌ ERRO: Arquivo não encontrado: '{arquivo}'")
        return None
    except json.JSONDecodeError as e:
        print(f"❌ ERRO: Arquivo '{arquivo}' não é um JSON válido: {e}")
        return None

    # Verificar estrutura básica de GeoJSON
    if not isinstance(data, dict):
        print(f"❌ ERRO: '{arquivo}' não é um objeto JSON válido")
        return None

    if data.get('type') != 'FeatureCollection':
        print(f"⚠️  AVISO: '{arquivo}' não é um FeatureCollection (type: {data.get('type')})")
        # Continuar mesmo assim, pois pode ser um Feature único

    if 'features' not in data:
        print(f"⚠️  AVISO: '{arquivo}' não contém campo 'features'")
        # Tentar extrair features mesmo assim
        return data

    return data


def extrair_features(data, arquivo):
    """Extrai as features de um GeoJSON.

    Args:
        data (dict): Dados do GeoJSON.
        arquivo (str): Nome do arquivo (para logging).

    Returns:
        list: Lista de features extraídas.
    """
    features = data.get('features', [])

    if not features:
        print(f"⚠️  AVISO: '{arquivo}' não contém features")
        return []

    # Validar que cada feature tem estrutura mínima
    features_validas = []
    for idx, feature in enumerate(features):
        if not isinstance(feature, dict):
            print(f"⚠️  AVISO: Feature {idx} em '{arquivo}' não é um objeto, ignorando")
            continue

        if 'geometry' not in feature:
            print(f"⚠️  AVISO: Feature {idx} em '{arquivo}' não tem geometria, ignorando")
            continue

        if 'properties' not in feature:
            print(f"⚠️  AVISO: Feature {idx} em '{arquivo}' não tem properties, ignorando")
            continue

        features_validas.append(feature)

    return features_validas


def validar_ids_unicos(features, emitir_aviso=True):
    """Valida se todos os IDs são únicos e reporta duplicatas.

    Args:
        features (list): Lista de features do GeoJSON.
        emitir_aviso (bool): Se True, imprime avisos de IDs duplicados.

    Returns:
        tuple: (ids_unicos_count, ids_duplicados_dict)
    """
    ids = []
    for feature in features:
        feature_id = feature.get('properties', {}).get('id')
        if feature_id is not None:
            ids.append(feature_id)

    # Contar ocorrências de cada ID
    id_counts = Counter(ids)
    ids_duplicados = {id_val: count for id_val, count in id_counts.items() if count > 1}

    if emitir_aviso and ids_duplicados:
        print(f"\n⚠️  AVISO: {len(ids_duplicados)} IDs duplicados encontrados:")
        print("-" * 60)
        for dup_id, count in sorted(ids_duplicados.items()):
            print(f"  ID '{dup_id}' aparece {count} vezes")
        print("-" * 60)
        print("💡 Execute o validador para identificar features específicas.")

    return len(set(ids)), ids_duplicados


def mesclar_geojson_files(arquivos_entrada, arquivo_saida, incluir_metadata=True):
    """Mescla múltiplos arquivos GeoJSON em um único FeatureCollection.

    Args:
        arquivos_entrada (list): Lista de caminhos para arquivos GeoJSON de entrada.
        arquivo_saida (str): Caminho para arquivo GeoJSON de saída.
        incluir_metadata (bool): Se True, adiciona campos metadata e CRS.

    Returns:
        bool: True se bem-sucedido, False caso contrário.
    """
    print(f"\n{'='*70}")
    print(f"  Mesclagem de Arquivos GeoJSON")
    print(f"{'='*70}\n")
    print(f"📂 Arquivos de entrada: {len(arquivos_entrada)}")

    todas_features = []

    # Processar cada arquivo de entrada
    for idx, arquivo in enumerate(arquivos_entrada, 1):
        print(f"\n[{idx}/{len(arquivos_entrada)}] Processando: {os.path.basename(arquivo)}")
        print("-" * 60)

        # Validar e carregar
        data = validar_geojson_entrada(arquivo)
        if data is None:
            print(f"❌ Pulando arquivo '{arquivo}' devido a erros")
            continue

        # Extrair features
        features = extrair_features(data, os.path.basename(arquivo))
        if not features:
            print(f"⚠️  Nenhuma feature válida encontrada em '{arquivo}'")
            continue

        # Reportar tipos de geometria
        geom_types = Counter(f.get('geometry', {}).get('type') for f in features)
        print(f"✅ {len(features)} features extraídas:")
        for geom_type, count in sorted(geom_types.items()):
            print(f"   - {geom_type}: {count}")

        todas_features.extend(features)

    # Verificar se conseguimos alguma feature
    if not todas_features:
        print(f"\n❌ ERRO: Nenhuma feature foi extraída dos arquivos de entrada")
        return False

    print(f"\n{'='*70}")
    print(f"📊 Resumo da Mesclagem")
    print(f"{'='*70}")
    print(f"Total de features: {len(todas_features)}")

    # Resumo de tipos de geometria
    geom_types_total = Counter(f.get('geometry', {}).get('type') for f in todas_features)
    print(f"Tipos de geometria:")
    for geom_type, count in sorted(geom_types_total.items()):
        print(f"  - {geom_type}: {count}")

    # Validar IDs únicos
    print(f"\nVerificando unicidade dos IDs...")
    ids_unicos, ids_duplicados = validar_ids_unicos(todas_features, emitir_aviso=True)
    print(f"IDs únicos: {ids_unicos}")

    # Construir GeoJSON de saída
    geojson_saida = {
        "type": "FeatureCollection",
        "features": todas_features
    }

    # Adicionar CRS e metadata se solicitado
    if incluir_metadata:
        geojson_saida["crs"] = {
            "type": "name",
            "properties": {
                "name": "urn:ogc:def:crs:EPSG::4674"
            }
        }

        # Data/hora no formato ISO8601 com timezone
        # Usando timezone -03:00 (horário de Brasília)
        agora = datetime.now(timezone.utc).astimezone()
        data_geracao = agora.strftime('%Y-%m-%dT%H:%M:%S%z')
        # Formatar timezone como -03:00 ao invés de -0300
        if len(data_geracao) > 2:
            data_geracao = data_geracao[:-2] + ':' + data_geracao[-2:]

        geojson_saida["metadata"] = {
            "schema_version": "R0",
            "data_geracao": data_geracao
        }

    # Salvar arquivo de saída
    try:
        with open(arquivo_saida, 'w', encoding='utf-8') as f:
            json.dump(geojson_saida, f, ensure_ascii=False, indent=2)
        print(f"\n✅ Arquivo mesclado salvo com sucesso!")
        print(f"📁 Arquivo de saída: {arquivo_saida}")

        # Calcular tamanho do arquivo
        tamanho = os.path.getsize(arquivo_saida)
        if tamanho < 1024:
            tamanho_str = f"{tamanho} bytes"
        elif tamanho < 1024 * 1024:
            tamanho_str = f"{tamanho / 1024:.1f} KB"
        else:
            tamanho_str = f"{tamanho / (1024 * 1024):.1f} MB"
        print(f"📏 Tamanho: {tamanho_str}")

    except IOError as e:
        print(f"\n❌ ERRO: Falha ao salvar arquivo de saída '{arquivo_saida}': {e}")
        return False

    # Avisos finais
    print(f"\n{'='*70}")
    if ids_duplicados:
        print(f"⚠️  ATENÇÃO: IDs duplicados foram detectados!")
        print(f"   Corrija antes de validar com validar_geojson.py")
    else:
        print(f"✅ Próximo passo: Validar com validar_geojson.py")
    print(f"{'='*70}\n")

    return True


def main():
    """Função principal do script."""
    parser = argparse.ArgumentParser(
        description='Mescla múltiplos arquivos GeoJSON em um único FeatureCollection',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemplos de uso:

  # Mesclar 2 arquivos:
  python mesclar_geojson.py points.geojson lines.geojson -o saida.geojson

  # Mesclar 3 arquivos com tipos diferentes:
  python mesclar_geojson.py \\
      L13_points.geojson \\
      L13_lines.geojson \\
      L13_polygons.geojson \\
      -o L13_conservacao_2026_R0.geojson

  # Mesclar sem adicionar metadata (apenas combinar features):
  python mesclar_geojson.py arq1.geojson arq2.geojson -o saida.geojson --no-metadata

Para mais informações, consulte a documentação do projeto.
        """
    )

    parser.add_argument(
        'arquivos',
        nargs='+',
        help='Arquivos GeoJSON para mesclar (pelo menos 2)'
    )

    parser.add_argument(
        '-o', '--output',
        required=True,
        help='Arquivo de saída (GeoJSON mesclado)'
    )

    parser.add_argument(
        '--no-metadata',
        action='store_true',
        help='Não adicionar campos CRS e metadata ao arquivo de saída'
    )

    args = parser.parse_args()

    # Validar argumentos
    if len(args.arquivos) < 2:
        print("❌ ERRO: São necessários pelo menos 2 arquivos para mesclar")
        sys.exit(1)

    # Verificar se arquivo de saída já existe
    if os.path.exists(args.output):
        resposta = input(f"⚠️  Arquivo '{args.output}' já existe. Sobrescrever? (s/N): ")
        if resposta.lower() not in ['s', 'sim', 'y', 'yes']:
            print("❌ Operação cancelada pelo usuário")
            sys.exit(0)

    # Executar mesclagem
    sucesso = mesclar_geojson_files(
        args.arquivos,
        args.output,
        incluir_metadata=not args.no_metadata
    )

    # Retornar código de saída
    sys.exit(0 if sucesso else 1)


if __name__ == '__main__':
    main()
