"""
Módulo de Correção de Dados RFID
Usa a nova tabela de correção em formato CSV
"""
import pandas as pd
import numpy as np
from typing import Dict, Optional

from .app_console import fc_log

# Orçamento de ligação (Threshold / FCC): EIRP máx. e sensibilidade na referência
# do modelo (-82 dBm). Max FCC Link Reversed não adiciona ganho fixo de antena.
FCC_MAX_EIRP_DBM = 36.02
# Limite EIRP para Max Link Forward com licença ETSI (alinhado ao simulador / regulamentação típica EU).
ETSI_MAX_LINK_FORWARD_EIRP_DBM = 33.0
READER_SENSITIVITY_DBM = -82.0


def max_link_forward_eirp_dbm_for_license(license_name: Optional[str]) -> float:
    """
    Retorna o teto de EIRP (dBm) usado no cálculo de Max FCC Link Forward conforme a licença ativa.
    Licenças cujo nome contém 'ETSI' usam 33,0 dBm; demais casos usam FCC_MAX_EIRP_DBM (36,02 dBm).
    """
    if license_name and "ETSI" in str(license_name).upper():
        return float(ETSI_MAX_LINK_FORWARD_EIRP_DBM)
    return float(FCC_MAX_EIRP_DBM)

# Backscatter no ramo reverso: RSSI' + (fator × atenuador) antes do path loss
# (POTR, conversion loss, Max FCC Link Reversed, RCS). Com calibração ativa: 1×;
# sem calibração ativa: 2× (Threshold e medições “sem cal”). Captura do perfil
# no módulo Calibration força 1× via parâmetro explícito (tabela de referência).
BACKSCATTER_ATTENUATOR_DB_FACTOR_CALIBRATED = 1.0
BACKSCATTER_ATTENUATOR_DB_FACTOR_UNCALIBRATED = 2.0

# Termo 20·log10(300/(4π)) dB com λ = 300/f_MHz (m); na especificação documental ≈ +27,55
_FSPL_REFERENCE_OFFSET_DB = 20.0 * np.log10(300.0 / (4.0 * np.pi))


def compute_max_fcc_link_reversed_m(
    potr_real_dbm: float,
    freq_mhz: float,
    reader_sensitivity_dbm: float = READER_SENSITIVITY_DBM,
) -> float:
    """
    Distância máxima do link reverso FCC (m): propagação Friis (uma ida) desde o plano da
    tag, com EIRP equivalente POTR_Real, até o nível reader_sensitivity no leitor.

    Forma fechada equivalente à especificação:
        D_rev = 10^((POTR_Real + 82 - 20·log10(f_MHz) + 27,55) / 20)
    quando reader_sensitivity_dbm = -82 dBm (usa READER_SENSITIVITY_DBM).

    Genericamente: D_rev = 10^((POTR_Real - S_rx - 20·log10(f_MHz) + 20·log10(300/(4π))) / 20).

    Independente do Max FCC Link Forward. POTR_Real segue apply_corrections_to_result
    (fator do atenuador no reverso: 1× com calibração ativa, 2× sem).
    """
    if freq_mhz is None or float(freq_mhz) <= 0:
        return 0.0
    f_mhz = float(freq_mhz)
    exponent = (
        float(potr_real_dbm)
        - float(reader_sensitivity_dbm)
        - 20.0 * np.log10(f_mhz)
        + _FSPL_REFERENCE_OFFSET_DB
    ) / 20.0
    return float(10.0**exponent)


# Carrega a tabela de correção
def load_correction_table() -> pd.DataFrame:
    """
    Carrega a tabela de correção do arquivo CSV
    
    Returns:
        DataFrame com frequências e fatores de correção
    """
    try:
        df = pd.read_csv('Tabela de Correção.txt')
        # Silencia mensagem para reduzir poluição
        return df
    except Exception as e:
        fc_log("correction_table", f"❌ Erro ao carregar tabela de correção: {e}")
        return pd.DataFrame()

# Variável global para armazenar a tabela
_correction_table = None

def get_correction_table() -> pd.DataFrame:
    """
    Retorna a tabela de correção (carrega se necessário)
    
    Returns:
        DataFrame com a tabela de correção
    """
    global _correction_table
    if _correction_table is None:
        _correction_table = load_correction_table()
    return _correction_table

def get_correction_factor(frequency_mhz: float) -> float:
    """
    Obtém o fator de correção para uma frequência específica
    
    Args:
        frequency_mhz: Frequência em MHz
        
    Returns:
        Fator de correção em dB
    """
    df = get_correction_table()
    if df.empty:
        return 0.0
    
    # Busca a frequência mais próxima
    freq_column = df.iloc[:, 0]
    correction_column = df.iloc[:, 1]
    
    # Se a frequência exata existe
    if frequency_mhz in freq_column.values:
        return correction_column[freq_column == frequency_mhz].iloc[0]
    
    # Interpolação linear para frequências intermediárias
    freq_values = freq_column.values
    correction_values = correction_column.values
    
    # Encontra os índices para interpolação
    if frequency_mhz < freq_values.min():
        return correction_values[0]
    elif frequency_mhz > freq_values.max():
        return correction_values[-1]
    else:
        # Encontra os dois pontos mais próximos
        idx = np.searchsorted(freq_values, frequency_mhz)
        if idx == 0:
            return correction_values[0]
        elif idx == len(freq_values):
            return correction_values[-1]
        else:
            # Interpolação linear
            x1, x2 = freq_values[idx-1], freq_values[idx]
            y1, y2 = correction_values[idx-1], correction_values[idx]
            return y1 + (y2 - y1) * (frequency_mhz - x1) / (x2 - x1)

def calculate_rcs(freq_mhz: float, distance: float, irradiated_power: float, backscatter: float, antenna_gain_db: float = 6.0) -> float:
    """
    Calcula o Radar Cross Section (RCS) usando a equação do radar
    
    Args:
        freq_mhz: Frequência em MHz
        distance: Distância em metros
        irradiated_power: Potência irradiada em dBm
        backscatter: Potência de backscatter em dBm
        antenna_gain_db: Ganho das antenas em dB (padrão: 6.0 dB)
        
    Returns:
        RCS em dBm² (decibéis relativos a 1 m²)
    """
    try:
        # Converte dBm para Watts
        pt_watts = 10**((irradiated_power - 30) / 10)  # dBm para Watts
        pr_watts = 10**((backscatter - 30) / 10)       # dBm para Watts
        
        # Calcula comprimento de onda
        c = 3e8  # Velocidade da luz em m/s
        freq_hz = freq_mhz * 1e6
        wavelength = c / freq_hz
        
        # Ganho das antenas (assumindo ganho igual para transmissão e recepção)
        gt = 10**(antenna_gain_db / 10)
        gr = 10**(antenna_gain_db / 10)
        
        # Fórmula do RCS: RCS = (4π)³ × R⁴ × Pr / (Pt × Gt × Gr × λ²)
        rcs_m2 = ((4 * 3.14159)**3 * (distance**4) * pr_watts) / (pt_watts * gt * gr * (wavelength**2))
        
        # Converte para dBm²: RCS_dBm2 = 10 * log10(RCS_m2)
        if rcs_m2 > 0:
            rcs_dbm2 = 10 * np.log10(rcs_m2)
        else:
            rcs_dbm2 = -999.0  # Valor para RCS zero ou inválido
        
        return rcs_dbm2
        
    except Exception as e:
        fc_log("correction_table", f"⚠️ Erro no cálculo do RCS: {e}")
        return -999.0

def apply_corrections_to_result(
    result: Dict,
    attenuator: float = 0.0,
    distance: float = 1.0,
    *,
    apply_profile_calibration: bool = True,
    backscatter_attenuator_db_factor: Optional[float] = None,
    max_link_forward_eirp_dbm: Optional[float] = None,
) -> Dict:
    """
    Deriva parâmetros de ligação RFID a partir de Module Power e RSSI medidos.

    Entradas base (por ponto / frequência):
        - freq_mhz, module_power (ou threshold_power), rssi (ou threshold_rssi)
        - Parâmetros de ensaio: attenuator (dB), distance (m)
        - Opcional: calibração ativa (offset forward/reverse por MHz) e fator da
          tabela CSV (correção por frequência).

    Referências do modelo (simplificado):
        - Module Power: nível reportado/configurado do leitor (caminho forward),
          antes do atenuador na cadeia modelada.
        - Atenuador: perda em dB entre saída modelada do leitor e antena; valor
          negativo representa ganho líquido (ex.: antena com ganho tratado como
          “-atenuador”).
        - Irradiated power: potência na referência de antena / EIRP de trabalho
          após atenuador (ou ganho) e correção de frequência (tabela).
        - RSSI: nível recebido no leitor; “backscatter” soma fator×atenuador até ao
          plano da antena: 1× com calibração ativa; 2× sem calibração ativa (override
          explícito com backscatter_attenuator_db_factor, p.ex. captura de perfil = 1×).
        - Path loss: Friis em espaço livre entre antena e tag, isotrópico–isotrópico:
          PL = 20*log10(4*pi*d/lambda), lambda = c/f = 300/f_MHz (m).

    Fórmulas (todas em dB ou dBm salvo indicação):
        1) module_power' = module_power + calib_forward_db   (se calibração ativa)
           rssi'         = rssi + calib_reverse_db

        2) irradiated_power = module_power' - attenuator + correction_factor
           (atenuador > 0 = perda; < 0 = ganho de antena; correction_factor da
           tabela por MHz; 0 se tabela vazia)

        3) backscatter = rssi' + k × attenuator, k = 1 se calibração ativa, senão k = 2
           (ou backscatter_attenuator_db_factor se passado)

        4) path_loss = 20 * log10(4 * pi * distance / wavelength)

        5) power_on_tag_forward  = irradiated_power - path_loss
           power_on_tag_reversed = backscatter + path_loss
           (potências equivalentes no plano da tag, forward e reverse, dadas as
           suposições Friis e os referenciais acima)

        6) conversion_loss = power_on_tag_reversed - power_on_tag_forward
           (diferença entre os dois ramos no plano da tag)

        7) Max FCC link forward (m): distância à qual, com EIRP limitada a
           max_link_forward_eirp_dbm (omissão: 36,02 dBm; licença ETSI no Threshold: 33 dBm),
           a potência na tag no forward seria a mesma que power_on_tag_forward medido no ensaio:
           PL_max = eirp_cap - power_on_tag_forward
           d_max = 10^(PL_max/20) * wavelength / (4*pi)

        8) Max FCC link reverse (m): POTR_Real = power_on_tag_reversed (no plano da tag),
           derivado de backscatter + FSPL conforme (3) — independente do link forward.
           Alcance até sensibilidade do leitor (Friis, uma ida; λ = 300/f_MHz):
           D_rev = 10^((POTR_Real - S_rx - 20·log10(f_MHz) + 20·log10(300/(4π))) / 20)
           (implementado em compute_max_fcc_link_reversed_m).

    Notas:
        - min_tag_power no código não entra nas expressões atuais de FCC forward.
        - RCS usa calculate_rcs() com ganho de antena 6 dB por omissão (só RCS).

    Args:
        result: Dicionário com freq_mhz e medições (module_power/rssi ou threshold_*).
        attenuator: Atenuador em dB (positivo = perda; negativo = ganho líquido).
        distance: Distância tag–antena em metros (espaço livre).
        apply_profile_calibration: Se False, não aplica offsets do perfil ativo (ex.: sweep do Calibration).
        backscatter_attenuator_db_factor: Se não None, multiplicador fixo do atenuador no
            backscatter. Se None, usa 1× com calibração ativa e 2× sem.
        max_link_forward_eirp_dbm: Teto de EIRP (dBm) para o alcance forward; None = FCC_MAX_EIRP_DBM.

    Returns:
        Dicionário com grandezas derivadas e cópias dos valores originais.
    """
    if not result:
        return result
    
    freq_mhz = result.get('freq_mhz', 0)
    # CORREÇÃO: Preserva dados originais do threshold
    module_power = result.get('threshold_power', result.get('module_power', 0))
    rssi = result.get('threshold_rssi', result.get('rssi', 0))

    # Mantém cópias explícitas dos valores originais
    module_power_original = float(module_power)
    rssi_original = float(rssi)

    # Obtém fator de correção da tabela
    correction_factor = get_correction_factor(freq_mhz)

    # Correções de calibração ativa (perfil salvo/carregado no módulo Calibration)
    calib_forward_db = 0.0
    calib_reverse_db = 0.0
    module_power = float(module_power_original)
    rssi = float(rssi_original)
    try:
        from .calibration_service import (
            has_active_calibration,
            get_correction_forward_dbm,
            get_correction_reverse_dbm,
        )
        if apply_profile_calibration and has_active_calibration():
            calib_forward_db = get_correction_forward_dbm(freq_mhz)
            calib_reverse_db = get_correction_reverse_dbm(freq_mhz)
            module_power = module_power_original + calib_forward_db
            rssi = rssi_original + calib_reverse_db
    except ImportError:
        pass

    if backscatter_attenuator_db_factor is not None:
        bs_att_factor = float(backscatter_attenuator_db_factor)
    else:
        try:
            from .calibration_service import has_active_calibration
            _cal_on = has_active_calibration()
        except ImportError:
            _cal_on = False
        bs_att_factor = (
            BACKSCATTER_ATTENUATOR_DB_FACTOR_CALIBRATED
            if _cal_on
            else BACKSCATTER_ATTENUATOR_DB_FACTOR_UNCALIBRATED
        )
    
    # Cálculos baseados nas fórmulas corretas, usando valores já corrigidos
    irradiated_power = module_power - attenuator + correction_factor
    backscatter = rssi + (bs_att_factor * attenuator)
    
    # Power on Tag calculations
    # Calcula path loss baseado na frequência e distância informada
    wavelength = 300 / freq_mhz  # Comprimento de onda em metros
    path_loss = 20 * np.log10(4 * np.pi * distance / wavelength)
    
    power_on_tag_forward = irradiated_power - path_loss
    power_on_tag_reversed = backscatter + path_loss

    # Conversion Loss
    conversion_loss = power_on_tag_reversed - power_on_tag_forward
    
    # FCC Link calculations (EIRP máx. forward configurável; sensibilidade -82 dBm pós-antena)
    wavelength = 300 / freq_mhz  # Comprimento de onda em metros
    fcc_power = (
        float(max_link_forward_eirp_dbm)
        if max_link_forward_eirp_dbm is not None
        else FCC_MAX_EIRP_DBM
    )
    min_tag_power = -10  # dBm
    reader_sensitivity = READER_SENSITIVITY_DBM
    
    # Max FCC Link Forward: limite EIRP vs potência na tag (forward) no ensaio
    max_path_loss_forward = fcc_power - power_on_tag_forward
    max_distance_forward = (10 ** (max_path_loss_forward / 20)) * wavelength / (4 * 3.14159)
    
    # Max FCC Link Reversed: POTR_Real = power_on_tag_reversed; alcance absoluto até S_rx (Friis)
    max_distance_reversed = compute_max_fcc_link_reversed_m(
        power_on_tag_reversed, float(freq_mhz), reader_sensitivity
    )
    
    # Cálculo do RCS (Radar Cross Section)
    rcs = calculate_rcs(freq_mhz, distance, irradiated_power, backscatter)
    
    # Retorna resultado com todas as correções
    return {
        'freq_mhz': freq_mhz,
        # CORREÇÃO: Preserva dados originais do threshold
        'threshold_power': result.get('threshold_power', module_power_original),  # Dados originais (não corrigidos)
        'threshold_rssi': result.get('threshold_rssi', rssi_original),  # Dados originais (não corrigidos)
        'module_power': module_power,  # Module Power possivelmente corrigido por calibração
        'rssi': rssi,  # RSSI possivelmente corrigido por calibração
        'irradiated_power': irradiated_power,  # Calculado com Module Power corrigido
        'backscatter': backscatter,
        'power_on_tag_forward': power_on_tag_forward,
        'power_on_tag_reversed': power_on_tag_reversed,
        'conversion_loss': conversion_loss,
        'max_fcc_link_forward': max_distance_forward,
        'max_fcc_link_reversed': max_distance_reversed,
        'rcs': rcs,  # NOVO: Radar Cross Section
        'correction_factor': correction_factor,  # Fator aplicado ao Module Power
        'calibration_forward_db': calib_forward_db,  # Correção aplicada ao caminho forward (dB)
        'calibration_reverse_db': calib_reverse_db,  # Correção aplicada ao caminho reverse (dB)
        'module_power_original': module_power_original,  # Copia explícita da potência original do módulo
        'rssi_original': rssi_original,  # Cópia explícita do RSSI original
        'attenuator': attenuator,
        'distance': distance
    }

def print_correction_info():
    """
    Imprime informações sobre a tabela de correção
    """
    df = get_correction_table()
    if not df.empty:
        fc_log("correction_table", f"📊 TABELA DE CORREÇÃO:")
        fc_log("correction_table", f"   Pontos: {len(df)}")
        fc_log("correction_table", f"   Faixa: {df.iloc[:, 0].min():.0f} - {df.iloc[:, 0].max():.0f} MHz")
        fc_log("correction_table", f"   Correção: {df.iloc[:, 1].min():.2f} a {df.iloc[:, 1].max():.2f} dB")
        fc_log("correction_table", f"   Média: {df.iloc[:, 1].mean():.2f} dB")
    else:
        fc_log("correction_table", "❌ Tabela de correção não disponível")

# Teste do módulo
if __name__ == "__main__":
    fc_log("correction_table", "🧪 TESTE DO MÓDULO DE CORREÇÃO")
    fc_log("correction_table", "=" * 40)
    
    print_correction_info()
    
    # Testa algumas frequências
    test_freqs = [800, 850, 900, 950, 1000]
    for freq in test_freqs:
        correction = get_correction_factor(freq)
        fc_log("correction_table", f"   {freq:4.0f} MHz: {correction:6.2f} dB")
    
    # Unidade: Max FCC Link Reversed (spec Threshold / Simulador)
    _d_rev_spec = compute_max_fcc_link_reversed_m(-23.0, 910.0, -82.0)
    assert abs(_d_rev_spec - 23.36) < 0.06, f" esperado ~23,36 m, obtido {_d_rev_spec:.4f}"
    fc_log("correction_table", f"   OK reverse link: 910 MHz, POTR -23 dBm → {_d_rev_spec:.2f} m (meta ~23,36 m)")

    # Testa aplicação de correção
    test_result = {
        'freq_mhz': 850,
        'module_power': 20.0,
        'rssi': -45.0
    }
    
    corrected = apply_corrections_to_result(test_result, attenuator=2.0, distance=1.0)
    fc_log("correction_table", f"\n📊 EXEMPLO DE CORREÇÃO:")
    fc_log("correction_table", f"   Frequência: {corrected['freq_mhz']} MHz")
    fc_log("correction_table", f"   Module Power Original: {test_result['module_power']} dBm")
    fc_log("correction_table", f"   Module Power Corrigido: {test_result['module_power'] + corrected['correction_factor']} dBm")
    fc_log("correction_table", f"   Irradiated Power: {corrected['irradiated_power']} dBm")
    fc_log("correction_table", f"   RSSI: {corrected['rssi']} dB (não corrigido)")
    fc_log("correction_table", f"   Backscatter: {corrected['backscatter']} dBm")
    fc_log("correction_table", f"   RCS: {corrected['rcs']:.1f} dBm²")
    fc_log("correction_table", f"   Fator Aplicado: {corrected['correction_factor']} dB")
