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

# SPDX-License-Identifier: BSD-3-Clause
# test_legacy_yang
# Copyright (C) 2025 Telecom Infra Project and GNPy contributors
# see AUTHORS.rst for a list of contributors

"""
Test conversion legacy to yang utils
====================================
check that combinations of inputs are correctly converted
"""
from pathlib import Path
import json
import subprocess    # nosec
import os
import pytest

from gnpy.tools.json_io import load_gnpy_json
from gnpy.tools.cli_examples import transmission_main_example, path_requests_run
from gnpy.tools.convert_legacy_yang import legacy_to_yang, yang_to_legacy
from gnpy.tools.yang_convert_utils import convert_delta_power_range


SRC_ROOT = Path(__file__).parent.parent
TEST_DIR = Path(__file__).parent
DATA_DIR = TEST_DIR / 'data' / 'convert'
EXAMPLE_DIR = SRC_ROOT / 'gnpy' / 'example-data'


@pytest.mark.parametrize('yang_input, expected_output, expected_autodesign, eqpt', [
    ('GNPy_yang_formatted-edfa_example_network.json', 'GNPy_legacy_formatted-edfa_example_network_expected.json',
     'edfa_example_network_autodesign_expected.json', None),
    ('GNPy_yang_formatted-testTopology_auto_design_expected.json',
     'GNPy_legacy_formatted-testTopology_auto_design_expected.json',
     'testTopology_auto_design_expected.json',
     'eqpt_config.json')])
def test_gnpy(tmpdir, yang_input, expected_output, expected_autodesign, eqpt):
    """Convert back from yang to legacy format, and checks that GNPy can run on the converted file
    """
    with open(DATA_DIR / yang_input, 'r', encoding='utf-8') as f:
        json_data = json.load(f)
        converted = yang_to_legacy(json_data)
    with open(DATA_DIR / expected_output, 'r', encoding='utf-8') as f:
        expected = json.load(f)
    assert converted == expected
    with open(tmpdir / 'essaitopo.json', 'w', encoding='utf-8') as f:
        json.dump(converted, f, indent=2, ensure_ascii=False)
    args = [str(tmpdir / 'essaitopo.json'), '--save-network', str(tmpdir / 'autodesign.json')]
    if eqpt is not None:
        args.append('-e')
        args.append(str(DATA_DIR / eqpt))
    transmission_main_example(args)
    expected = load_gnpy_json(DATA_DIR / expected_autodesign)
    actual = load_gnpy_json(tmpdir / 'autodesign.json')
    assert actual == expected


@pytest.mark.parametrize('topo_input, service_input, expected_autodesign, eqpt', [
    ('testTopology_auto_design.json', 'testTopology_testservices.json',
     'testTopology_auto_design_expected.json', 'eqpt_config.json')])
def test_gnpy_path_requests_run(tmpdir, topo_input, service_input, expected_autodesign, eqpt):
    """Convert to and back from legacy- to yang to legacy format and checks that conversion runs with gnpy
    """
    with open(DATA_DIR / topo_input, 'r', encoding='utf-8') as f:
        json_data = json.load(f)
        converted = yang_to_legacy(legacy_to_yang(json_data))
    with open(tmpdir / 'essaitopo.json', 'w', encoding='utf-8') as f:
        json.dump(converted, f, indent=2, ensure_ascii=False)
    with open(DATA_DIR / service_input, 'r', encoding='utf-8') as f:
        json_data = json.load(f)
        converted = yang_to_legacy(legacy_to_yang(json_data))
    with open(tmpdir / 'essaiservice.json', 'w', encoding='utf-8') as f:
        json.dump(converted, f, indent=2, ensure_ascii=False)
    with open(DATA_DIR / eqpt, 'r', encoding='utf-8') as f:
        json_data = json.load(f)
        converted = yang_to_legacy(legacy_to_yang(json_data))
    with open(tmpdir / 'essaieqpt.json', 'w', encoding='utf-8') as f:
        json.dump(converted, f, indent=2, ensure_ascii=False)
    args = [str(tmpdir / 'essaitopo.json'), str(tmpdir / 'essaiservice.json'),
            '-e', str(tmpdir / 'essaieqpt.json'), '--save-network', str(tmpdir / 'autodesign.json')]
    path_requests_run(args)
    expected = load_gnpy_json(DATA_DIR / expected_autodesign)
    actual = load_gnpy_json(tmpdir / 'autodesign.json')
    assert actual == expected


def test_gnpy_eqpt():
    """Convert back from yang to legacy format and checks that conversion runs with gnpy
    """
    json_data = {
        "Edfa": [{
            "type_variety": "std_medium_gain",
            "type_def": "gnpy-eqpt-config:variable_gain",
            "gain_flatmax": "26.0",
            "gain_min": "15",
            "p_max": "21",
            "nf_min": "6",
            "nf_max": "10",
            "out_voa_auto": False,
            "allowed_for_design": True}],
        "Fiber": [{
            "type_variety": "SSMF",
            "dispersion": "0.0000167",
            "effective_area": "0.0000000000830",
            "pmd_coef": "0.000000000000001265"}],
        "Span": [{
            "power_mode": False,
            "delta_power_range_dict_db": {"min_value": "0.0", "max_value": "0.0", "step": "0.5"},
            "max_fiber_lineic_loss_for_raman": "0.25",
            "target_extended_gain": "2.5",
            "max_length": "150",
            "length_units": "km",
            "max_loss": "28",
            "padding": "10",
            "EOL": "0",
            "con_in": "0",
            "con_out": "0"}],
        "Roadm": [{
            "type_variety": "example_test",
            "target_pch_out_db": "-18.0",
            "add_drop_osnr": "35.0",
            "pmd": "0.000000000001",
            "pdl": "0.5",
            "restrictions": {
                "preamp_variety_list": [],
                "booster_variety_list": []
            },
            "roadm-path-impairments": []}],
        "SI": [{
            "f_min": "191300000000000.0",
            "f_max": "196100000000000.0",
            "baud_rate": "32000000000.0",
            "spacing": "50000000000.0",
            "power_dbm": "0.0",
            "power_range_dict_db": {"min_value": "0.0", "max_value": "0.0", "step": "0.5"},
            "roll_off": "0.15",
            "tx_osnr": "100.0",
            "sys_margins": "0.0"}],
        "Transceiver": [{
            "type_variety": "vendorA_trx-type1",
            "frequency": {
                "min": "191350000000000",
                "max": "196100000000000"
            },
            "mode": [{
                "format": "PS_SP64_1",
                "baud_rate": "32000000000",
                "OSNR": "11",
                "bit_rate": "100000000000.0",
                "roll_off": "0.15",
                "tx_osnr": "100",
                "min_spacing": "50000000000",
                "cost": "1"
            }, {
                "format": "PS_SP64_2",
                "baud_rate": "64000000000",
                "OSNR": "15",
                "bit_rate": "200000000000",
                "roll_off": "0.15",
                "tx_osnr": "100",
                "min_spacing": "75000000000",
                "cost": "1"
            }
            ]}]}

    expected_data = {
        "Edfa": [{
            "type_variety": "std_medium_gain",
            "type_def": "variable_gain",
            "gain_flatmax": 26.0,
            "gain_min": 15.0,
            "p_max": 21.0,
            "nf_min": 6.0,
            "nf_max": 10.0,
            "out_voa_auto": False,
            "allowed_for_design": True}],
        "Fiber": [{
            "type_variety": "SSMF",
            "dispersion": 1.67e-05,
            "effective_area": 83e-12,
            "pmd_coef": 1.265e-15}],
        "Span": [{
            "power_mode": False,
            "delta_power_range_db": [0.0, 0.0, 0.5],
            "max_fiber_lineic_loss_for_raman": 0.25,
            "target_extended_gain": 2.5,
            "max_length": 150.0,
            "length_units": "km",
            "max_loss": 28.0,
            "padding": 10.0,
            "EOL": 0.0,
            "con_in": 0.0,
            "con_out": 0.0}],
        "Roadm": [{
            "type_variety": "example_test",
            "target_pch_out_db": -18.0,
            "add_drop_osnr": 35.0,
            "pmd": 1e-12,
            "pdl": 0.5,
            "restrictions": {
                "preamp_variety_list": [],
                "booster_variety_list": []
            },
            "roadm-path-impairments": []}],
        "SI": [{
            "f_min": 191.3e12,
            "f_max": 196.1e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0.0,
            "power_range_db": [0.0, 0.0, 0.5],
            "roll_off": 0.15,
            "tx_osnr": 100.0,
            "sys_margins": 0.0}],
        "Transceiver": [{
            "type_variety": "vendorA_trx-type1",
            "frequency": {
                "min": 191.35e12,
                "max": 196.1e12
            },
            "mode": [{
                "format": "PS_SP64_1",
                "baud_rate": 32e9,
                "OSNR": 11,
                "bit_rate": 100e9,
                "roll_off": 0.15,
                "tx_osnr": 100,
                "min_spacing": 50e9,
                "cost": 1
            }, {
                "format": "PS_SP64_2",
                "baud_rate": 64e9,
                "OSNR": 15,
                "bit_rate": 200e9,
                "roll_off": 0.15,
                "tx_osnr": 100,
                "min_spacing": 75e9,
                "cost": 1
            }]
        }]
    }

    converted = yang_to_legacy(json_data)
    assert converted == expected_data


@pytest.mark.parametrize('input, expected_output', [
    ('edfa_example_network.json', 'GNPy_yang_formatted-edfa_example_network_expected.json'),
    ('testTopology_auto_design.json', 'GNPy_yang_formatted-testTopology_auto_design_expected.json'),
    ('GNPy_yang_formatted-testTopology_auto_design_expected.json',
     'GNPy_yang_formatted-testTopology_auto_design_expected.json'),
    ('testTopology_testservices.json', 'testTopology_testservices_expected.json'),
    ('testTopology_testservices_expected.json', 'testTopology_testservices_expected.json'),
    ('eqpt_config.json', 'GNPy_yang_formatted-eqpt_config_expected.json'),
    ('extra_eqpt_config.json', 'GNPy_yang_formatted-extra_eqpt_config_expected.json'),
    ('GNPy_yang_formatted-eqpt_config_expected.json', 'GNPy_yang_formatted-eqpt_config_expected.json'),
    ('sim_params.json', 'GNPy_yang_formatted-sim_params_expected.json'),
    ('GNPy_yang_formatted-sim_params_expected.json', 'GNPy_yang_formatted-sim_params_expected.json'),
    ('spectrum.json', 'GNPy_yang_formatted-spectrum_expected.json'),
    ('GNPy_yang_formatted-spectrum_expected.json', 'GNPy_yang_formatted-spectrum_expected.json'),
    ('testTopology_response.json', 'GNPy_yang_formatted-testTopology_response.json'),
    ('std_medium_gain_advanced_config.json', 'GNPy_yang_formatted-edfa-config_expected.json')])
def test_gnpy_convert(input, expected_output):
    """Convert legacy gnpy format to yang format and checks that conversion is as expected
    """
    with open(DATA_DIR / input, 'r', encoding='utf-8') as f:
        json_data = json.load(f)
        actual = legacy_to_yang(json_data)
    with open(DATA_DIR / expected_output, 'r', encoding='utf-8') as f:
        expected = json.load(f)
    assert actual == expected


@pytest.mark.parametrize("args, fileout", (
    [['--legacy-to-yang', EXAMPLE_DIR / 'edfa_example_network.json', '-o'], 'essaitopo.json'],
    [['--legacy-to-yang', DATA_DIR / 'testTopology_auto_design.json', '-o'], 'essaitopo2.json'],
    [['--legacy-to-yang', DATA_DIR / 'eqpt_config.json', '-o'], 'essaieqpt.json'],
    [['--legacy-to-yang', DATA_DIR / 'testTopology_testservices.json', '-o'], 'essaireq.json']))
def test_example_invocation(tmpdir, args, fileout):
    """test the main function of converter"""
    os.environ['PYTHONPATH'] = str(SRC_ROOT)
    proc = subprocess.run(
        ('python', SRC_ROOT / 'gnpy' / 'tools' / 'convert_legacy_yang.py', *args, tmpdir / fileout),
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, universal_newlines=True)     # nosec
    assert proc.stderr == ''


@pytest.mark.parametrize("args", (
    [['--validate', DATA_DIR / 'GNPy_api_example.json']]))
def test_example_invocation2(args):
    """test the main function of converter"""
    os.environ['PYTHONPATH'] = str(SRC_ROOT)
    proc = subprocess.run(
        ('python', SRC_ROOT / 'gnpy' / 'tools' / 'convert_legacy_yang.py', *args),
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, universal_newlines=True)     # nosec
    assert proc.stderr == ''


def test_span_with_delta_power_range_dict_db():
    """Test when span already has delta_power_range_dict_db"""
    input_data = {
        'Span': [{
            "power_mode": False,
            "max_fiber_lineic_loss_for_raman": 0.25,
            "target_extended_gain": 2.5,
            "max_length": 150,
            "length_units": "km",
            "max_loss": 28,
            "padding": 10,
            "EOL": 0,
            "con_in": 0,
            "con_out": 0,
            'delta_power_range_dict_db': {
                'min_value': -5,
                'max_value': 5,
                'step': 0.1
            }
        }]
    }
    result = convert_delta_power_range(input_data)
    assert result == input_data


def test_span_missing_both_power_ranges():
    """Test when span is missing both power range formats"""
    input_data = {
        'Span': [{
            "power_mode": False,
            "max_fiber_lineic_loss_for_raman": 0.25,
            "target_extended_gain": 2.5,
            "max_length": 150,
            "length_units": "km",
            "max_loss": 28,
            "padding": 10,
            "EOL": 0,
            "con_in": 0,
            "con_out": 0
        }]
    }
    with pytest.raises(KeyError) as exc:
        convert_delta_power_range(input_data)
    assert 'delta_power_range or delta_power_range_dict_db missing' in str(exc.value)


def test_si_with_power_range_dict_db():
    """Test when SI already has power_range_dict_db"""
    input_data = {
        'SI': [{
            "f_min": 191.3e12,
            "f_max": 196.1e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0,
            'power_range_dict_db': {
                'min_value': -10,
                'max_value': 10,
                'step': 0.5
            }
        }]
    }
    result = convert_delta_power_range(input_data)
    assert result == input_data


def test_si_missing_both_power_ranges():
    """Test when SI is missing both power range formats"""
    input_data = {
        'SI': [{
            "f_min": 191.3e12,
            "f_max": 196.1e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0
        }]
    }
    with pytest.raises(KeyError) as exc:
        convert_delta_power_range(input_data)
    assert 'power_range_db or power_range_dict_db missing' in str(exc.value)


def test_successful_conversion():
    """Test successful conversion of both Span and SI"""
    input_data = {
        'Span': [{
            "power_mode": False,
            'delta_power_range_db': [-5, 5, 0.1],
            "max_fiber_lineic_loss_for_raman": 0.25,
            "target_extended_gain": 2.5,
            "max_length": 150,
            "length_units": "km",
            "max_loss": 28,
            "padding": 10,
            "EOL": 0,
            "con_in": 0,
            "con_out": 0
        }],
        'SI': [{
            "f_min": 191.3e12,
            "f_max": 196.1e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            'power_range_db': [-10, 10, 0.5],
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0
        }, {
            "type_variety": "LBAND",
            "f_min": 186.0e12,
            "f_max": 190.0e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            'power_range_db': [0, 0, 0.5],
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0
        }]
    }

    expected_output = {
        'Span': [{
            "power_mode": False,
            "max_fiber_lineic_loss_for_raman": 0.25,
            "target_extended_gain": 2.5,
            "max_length": 150,
            "length_units": "km",
            "max_loss": 28,
            "padding": 10,
            "EOL": 0,
            "con_in": 0,
            "con_out": 0,
            'delta_power_range_dict_db': {
                'min_value': -5,
                'max_value': 5,
                'step': 0.1
            }
        }],
        'SI': [{
            "f_min": 191.3e12,
            "f_max": 196.1e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            'power_range_dict_db': {
                'min_value': -10,
                'max_value': 10,
                'step': 0.5
            },
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0
        }, {
            "type_variety": "LBAND",
            "f_min": 186.0e12,
            "f_max": 190.0e12,
            "baud_rate": 32e9,
            "spacing": 50e9,
            "power_dbm": 0,
            'power_range_dict_db': {
                'min_value': 0,
                'max_value': 0,
                'step': 0.5
            },
            "roll_off": 0.15,
            "tx_osnr": 100,
            "sys_margins": 0
        }]
    }

    result = convert_delta_power_range(input_data)
    assert result == expected_output
