from typing import Dict
import os
from dotenv import load_dotenv
from opencage.geocoder import OpenCageGeocode

from .country_ml import CountryMl
from .location_local_constants import LocationLocalConstants
from .point import Point
from .util import LocationsUtil

load_dotenv()
from database_mysql_local.generic_crud import GenericCRUD  # noqa402
from logger_local.Logger import Logger  # noqa: E402
from language_remote.lang_code import LangCode  # noqa: E402
from user_context_remote.user_context import UserContext  # noqa: E402
# TODO Please use python-sdk fuction to get the environment variable
# TODO Please change to LOCATION_OPENCAGE_KEY everywhere
api_key = os.getenv("OPENCAGE_KEY")

logger = Logger.create_logger(
    object=LocationLocalConstants.OBJECT_FOR_LOGGER_CODE)
user_context = UserContext()


# TODO Can we use GenericMl
# TODO Rename the class name to CountriesLocal
# TODO Create Country class which store one country
# TODO Please add is_test_data parameter
class Country(GenericCRUD):
    country_ml = CountryMl()

    def __init__(self):
        logger.start("start init Country")

        GenericCRUD.__init__(
            self,
            default_schema_name=LocationLocalConstants.LOCATION_SCHEMA_NAME,
            default_table_name=LocationLocalConstants.COUNTRY_TABLE_NAME,
            default_view_table_name=LocationLocalConstants.COUNTRY_VIEW_NAME,
            default_id_column_name=LocationLocalConstants.COUNTRY_ID_COLUMN_NAME)  # noqa501

        logger.end("end init Country")

    def insert(self, country: str, lang_code: LangCode = None,
               title_approved: bool = False,
               new_country_data: Dict[str, any] = None,
               coordinate: Point = None) -> int:
        logger.start("start insert country",
                     object={'coordinate': coordinate, 'country': country,
                             'lang_code': lang_code,
                             'title_approved': title_approved,
                             'new_country_data': new_country_data})
        is_valid = LocationsUtil.validate_insert_args(
            name=country, lang_code=lang_code, title_approved=title_approved, coordinate=coordinate)
        if not is_valid:
            logger.end(log_message="Country was not inserted because no country name was provided")
            return None
        lang_code = lang_code or user_context.get_effective_profile_preferred_lang_code()
        new_country_data = new_country_data or {}
        try:
            country_json = {
                key: value for key, value in {
                    'coordinate': coordinate,
                    'iso': new_country_data.get("iso"),
                    'name': country,
                    'nicename': new_country_data.get("nicename"),
                    'iso3': new_country_data.get("iso3"),
                    'numcode': new_country_data.get("numcode"),
                    'phonecode': new_country_data.get("phonecode")
                }.items() if value is not None
            }
            # TODO Can we use GenericMl
            country_id = GenericCRUD.insert(self, data_json=country_json)

        except Exception as e:
            logger.exception("error in insert country")
            logger.end()
            raise e
        try:
            country_ml_id = self.country_ml.insert(
                country=country,
                country_id=country_id,
                lang_code=lang_code,
                title_approved=title_approved)
        # TODO Replace e with exception everywhere
        except Exception as e:
            logger.exception("error in insert country")
            logger.end()
            raise e
        logger.end("end insert country",
                   object={'country_id': country_id,
                           'country_ml_id': country_ml_id})
        return country_id

    # TODO: the read function is duplicated in all classes
    def read(self, location_id: int):
        logger.start("start read location",
                     object={'location_id': location_id})
        result = GenericCRUD.select_one_dict_by_id(
            self,
            id_column_value=location_id,
            select_clause_value=LocationLocalConstants.COUNTRY_TABLE_COLUMNS)

        result = LocationsUtil.extract_coordinates_and_replace_by_point(
            data_json=result)
        logger.end("end read location",
                   object={"result": result})
        return result

    @staticmethod
    def get_country_id_by_country_name(country_name: str, lang_code: LangCode = None) -> int:
        logger.start("start get_country_id_by_country_name",
                     object={'country_name': country_name})
        if country_name is None:
            logger.end(log_message="end get_country_id_by_country_name",
                       object={'country_id': None})
            return None
        LangCode.validate(lang_code)
        lang_code = lang_code or user_context.get_effective_profile_preferred_lang_code()
        where_clause = f"title='{country_name}' AND lang_code='{lang_code.value}'"

        country_id_json = Country.country_ml.select_one_dict_by_where(
            select_clause_value=LocationLocalConstants.COUNTRY_ID_COLUMN_NAME,
            where=where_clause,
            order_by="country_id DESC")
        country_id = country_id_json.get(
            LocationLocalConstants.COUNTRY_ID_COLUMN_NAME)

        logger.end("end get_country_id_by_country_name",
                   object={'country_id': country_id})
        return country_id

    @staticmethod
    # TODO Please add paramete types and return types to all methods/functions
    def get_country_name(location):
        # Create a geocoder instance
        logger.start("start get_country_name",
                     object={'location': location})

        # Define the city or state
        # TODO Please create new class GeoCode and GeoCodeOpenCage Class which interit from GeoCode Class.
        # Please move those calls to the GeoCodeOpenCage.geocode(location).
        # TODO Please add calls to ApiManagement Indirect from GeoCode class
        # TODO Shall we call OpenCageGeocode(api_key) everytime or we can do it one time globally?
        geocoder = OpenCageGeocode(api_key)

        # Use geocoding to get the location details
        results = geocoder.geocode(location)

        if results and len(results) > 0:
            first_result = results[0]
            components = first_result['components']

            # Extract the country from components
            country_name = components.get('country', '')
            if not country_name:
                # If country is not found, check for country_code
                # as an alternative
                country_name = components.get('country_code', '')
        else:
            country_name = None
            logger.error("country didnt  found for %s." % location)
        logger.end("end get_country_name",
                   object={'country_name': country_name})
        return country_name
