import pytest
import pandas as pd
import importlib
import numpy as np
from contextvars import copy_context
import dash
from dash._callback_context import context_value
from dash._utils import AttributeDict
code = importlib.import_module('tabs.evaluation_callbacks')
from map_handler import START_DATE, END_DATE

#add equality checker for objects
def __eq__(self, other):
    if isinstance(self, other.__class__):
        return self.a == other.a and self.b == other.b
    return False

#============ TEST extend_l============================================
def test_extend_l():
    assert code.extend_l([[1],[2],[3]]) == [1,2,3]

#============ TEST render_evaluation_tab=================================
def test_render_evaluation_tab():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": [{"prop_id": "nrt-evaluation.n_clicks"},{"prop_id": "scores-evaluation.n_clicks"}]}))
        return code.render_evaluation_tab(0, 0) 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0] == dash.no_update
    assert output[1:] == ({ 'fontWeight': 'bold' }, { 'fontWeight': 'normal' })


#============ TEST _no_data_modis========================================
def test_no_data_modis():
    assert code._no_modis_data() == ([{'name': 'NO DATA', 'id': ''}], [], {'display': 'block'})

#============ TEST update_time_selection===================================
def test_update_time_selection_seasonal():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": [{"prop_id": "obs-timescale-dropdown.value"},{"prop_id": "obs-network-dropdown.value"}]}))
        return code.update_time_selection('seasonal', 'modis') 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert "'Spring 2018', 'value': '201803-201805'}], 'Select season')" in str(output)

def test_update_time_selection_annual():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": [{"prop_id": "obs-timescale-dropdown.value"},{"prop_id": "obs-network-dropdown.value"}]}))
        return code.update_time_selection('annual', 'modis') 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert "{'label': '2018', 'value': '201801-201812'}], 'Select year')" in str(output)


def test_update_time_selection_monthly():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": [{"prop_id": "obs-timescale-dropdown.value"},{"prop_id": "obs-network-dropdown.value"}]}))
        return code.update_time_selection('monthly', 'modis') 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert "{'label': 'April 2023', 'value': '202304'}, {'label': 'March 2023', 'value': '202303'}, {'label': 'February 2023', 'value': '202302'}, {'label': 'January 2023', 'value': '202301'}" in str(output)
#============ TEST modis_scores_tables_retrieve========================
def test_modis_scores_tables_retrieve_no_n_click():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.modis_scores_tables_retrieve(0, ['median'],['bias'],'modis','monthly','202208' ) 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0] == dash.no_update
    assert output[1] == dash.no_update
    assert output[2] == {'display':'none'}

def test_modis_scores_tables_retrieve_median_202206():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.modis_scores_tables_retrieve(1, ['median'],['bias'],'modis','monthly','202206' ) 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output == ([{'id': 'model', 'name': ''}, {'id': 'bias', 'name': 'MBE'}], [{'bias': '-0.09', 'model': 'MULTI-MODEL'}], {'display': 'block'}) 

def test_modis_scores_tables_retrieve_no_data():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.modis_scores_tables_retrieve(1, ['median'],['bias'],'modis','monthly','212208' ) 

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output == ([{'id': '', 'name': 'NO DATA'}], [], {'display': 'block'})


#============ TEST aeronet_scores_tables_retrieve========================
def test_aeronet_scores_tables_retrieve_no_n_click():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.aeronet_scores_tables_retrieve(0, None, None, None, None, None, ['median'], ['bias'], 'aeronet', 'monthly', '202208', [], [], None, [], [], None, [], [], None, [], [], None,[], [], None)  
    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0] == dash.no_update
    assert output[1] == dash.no_update
    assert output[2] == {'display':'none'}

def test_aeronet_scores_tables_retrieve_median_202206():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.aeronet_scores_tables_retrieve(1, None, None, None, None, None, ['median'], ['bias'], 'aeronet', 'monthly', '202208', [], [], None, [], [], None, [], [], None, [], [], None,[], [], None)  
    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0] == [{'id': 'station', 'name': ['MBE', '']}, {'id': 'median', 'name': ['MBE', 'MULTI-MODEL']}]
    assert output[1] == [{'median': '-0.17', 'station': 'Europe'}, {'median': '-0.11', 'station': 'Mediterranean'}, {'median': '-0.31', 'station': 'MiddleEast'}, {'median': '-0.03', 'station': 'NAfrica'}, {'median': '-0.11', 'station': 'Total'}]
 

def test_aeronet_scores_tables_retrieve_no_data():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "scores-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.value"},
                {"prop_id": "obs-statistics-dropdown.value"},
                {"prop_id": "obs-network-dropdown.value"},
                {"prop_id": "obs-timescale-dropdown.value"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.aeronet_scores_tables_retrieve(1, None, None, None, None, None, ['median'], ['bias'], 'aeronet', 'monthly', '212208', [], [], None, [], [], None, [], [], None, [], [], None,[], [], None)  

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0] == [{'id': 'station', 'name': ['NO DATA']},{'id': 'station', 'name': ['NO DATA']}, {'id': 'station', 'name': ['NO DATA']},  {'id': 'station', 'name': ['NO DATA']}, {'id': 'station', 'name': ['NO DATA']}]

#=====================test show eval modis timeseries===============================
# def test_show_eval_modis_timeseries():
#     def run_callback():
#         #df_input is not the correct format and needs to be changed
#         df_input = {'lon': {'10': 10.949999809265137}, 'lat': {'10': 44.630001068115234}, 'stations': {'10': 'Modena'}}
#         context_value.set(AttributeDict(**{"triggered_inputs": 
#             [{"prop_id": "ts-eval-modis-button.n_clicks"},
#                 {"prop_id": "modis-clicked-coords.data"},
#                 {"prop_id": "eval-date-picker.date"},
#                 {"prop_id": "obs-dropdown.value"},
#                 {"prop_id": "obs-mod-dropdown.value"}]}))
#         return code.show_eval_modis_timeseries.uncached(1, df_input, START_DATE, 'aeronet', 'median')
#
#     ctx = copy_context()
#     output = ctx.run(run_callback)
#     assert output == 0

#=====================test show aeronet modis timeseries===============================
def test_show_eval_aeronet_timeseries():
    def run_callback():
        df_input = {'lon': {'10': 10.949999809265137}, 'lat': {'10': 44.630001068115234}, 'stations': {'10': 'Modena'}}
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "ts-eval-button.n_clicks"},
                {"prop_id": "stations-clicked-coords.data"},
                {"prop_id": "eval-date-picker.start_date"},
                {"prop_id": "eval-date-picker.end_date"},
                {"prop_id": "obs-dropdown.value"},
                {"prop_id": "obs-mod-dropdown.value"}]}))
        return code.show_eval_aeronet_timeseries.uncached(1, df_input, START_DATE, END_DATE, 'aeronet', 'monarch')

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0].children.id == 'timeseries-eval-modal'
    assert output[0].children.config == {'displayModeBar': True, 'displaylogo': False, 'modeBarButtonsToRemove': ['zoom2d', 'pan2d', 'select2d', 'lasso2d', 'autoScale2d']}
    assert output[1] == True
    
#===================== test update_eval_aeronet ===============================
def test_update_eval_aeronet():
    def run_callback():
        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "eval-apply.n_clicks"},
                {"prop_id": "obs-models-dropdown.start_date"},
                {"prop_id": "obs-models-dropdown._date"},
                {"prop_id": "obs-selection-dropdown.value"}]}))
        return code.update_eval_aeronet.uncached(1, START_DATE, END_DATE, 'aeronet')  

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert list(output[0].keys()) == ['lon', 'lat', 'stations']
    assert output[1].id == {'index': 'None', 'tag': 'empty-map'}
    assert output[1].children[0].id == {'tag': 'empty-tile-layer', 'index': 'None'}
    assert output[0]['stations'][88] == 'REIMS_GSMA'
    assert output[0]['lat'][88] == 49.2400016784668
    assert output[0]['lon'][88] == 4.070000171661377
    assert "<a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors &copy; <a href='https://carto.com/attributions'>CARTO</a>" in str(output[1])

#===================== test update_eval_modis ===============================
def test_update_eval_modis():
    def run_callback():
        mod_div = {'props': {'children': [{'props': {'children': None, 'id': {'tag': 'empty-tile-layer', 'index': 'None'}, 'attribution': "&copy; <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors &copy; <a href='https://carto.com/attributions'>CARTO</a>", 'url': 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'}, 'type': 'TileLayer', 'namespace': 'dash_leaflet'}, {'props': {'position': 'topright'}, 'type': 'FullscreenControl', 'namespace': 'dash_leaflet'}, None, None, None, None], 'id': {'tag': 'empty-map', 'index': 'None'}, 'animate': False, 'center': [30, 15], 'inertia': True, 'minZoom': 2, 'preferCanvas': True, 'wheelDebounceTime': 80, 'wheelPxPerZoomLevel': 120, 'zoom': 2.9, 'zoomSnap': 0.1, 'bounds': [[-24.686952411999144, -31.640625000000004], [65.87472467098549, 61.52343750000001]]}, 'type': 'Map', 'namespace': 'dash_leaflet'}

        context_value.set(AttributeDict(**{"triggered_inputs": 
            [{"prop_id": "eval-apply.n_clicks"},
                {"prop_id": "eval-date-picker.date"},
                {"prop_id": "obs-models-dropdown.date"},
                {"prop_id": "obs-dropdown.value"},
                {"prop_id": "graph-eval-modis-mod.children"}]}))
        return code.update_eval_modis.uncached(1, START_DATE, 'median', 'modis', mod_div)  

    ctx = copy_context()
    output = ctx.run(run_callback)
    assert output[0].id == {'index': 'None', 'tag': 'modis-map'}
    assert output[0].children[0].id == {'tag': 'modis-tile-layer', 'index': 'None'}
    assert output[0].children[0].attribution == "&copy; <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors &copy; <a href='https://carto.com/attributions'>CARTO</a>"
    assert output[1].children[0].url == 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
    assert output[1].id == {'index': 'None', 'tag': 'empty-map'}

#===================== test update_eval('modis') ===============================
def test_update_eval_test_modis():
    assert str(code.update_eval.uncached('modis')[0][0]) == "Label('Date')"
    assert code.update_eval.uncached('modis')[0][1].id == "eval-date-picker"
    assert code.update_eval.uncached('modis')[0][1].display_format == 'DD MMM YYYY'

    assert " zoomSnap=0.1), id='graph-eval-modis-obs'), Div(children=P('Aerosol data ©2023 WMO Barcelona Dust Regional Center, NASA.')" in str(code.update_eval.uncached('modis')[1][0]) 
    assert str(code.update_eval.uncached('modis')[2]) == "modis"
    assert str(code.update_eval.uncached('modis')[3]) == "{'display': 'table-cell'}"

#===================== test update_eval('aeronet') ===============================
def test_update_eval_test_aeronet():
    assert str(code.update_eval.uncached('aeronet')[0][0]) == "Label('Date range')"
    assert code.update_eval.uncached('aeronet')[0][1].id == "eval-date-picker"
    assert code.update_eval.uncached('aeronet')[0][1].display_format == 'DD MMM YYYY'

    assert "id='graph-eval-aeronet'), Div(children=P('Aerosol data ©2023 WMO Barcelona Dust Regional Center, NASA.'), id='eval-aeronet-disclaimer', className='disclaimer" in str(code.update_eval.uncached('aeronet')[1]) 
    assert str(code.update_eval.uncached('aeronet')[2]) == "aeronet"
    assert str(code.update_eval.uncached('aeronet')[3]) == "{'display': 'none'}"
