from typing import Dict
try:
    # When running from this package
    from location_local_constants import LocationLocalConstants
    from neighborhood import Neighborhood
    from county import County
    from region import Region
    from state import State
    from country import Country
except Exception:
    # When importing this module from another package
    from location_local.location_local_constants import LocationLocalConstants
    from location_local.neighborhood import Neighborhood
    from location_local.county import County
    from location_local.region import Region
    from location_local.state import State
    from location_local.country import Country
from dotenv import load_dotenv
load_dotenv()
from circles_local_database_python.generic_crud import GenericCRUD   # noqa: E402
from circles_local_database_python.connector import Connector   # noqa: E402
from logger_local.Logger import Logger  # noqa: E402
from user_context_remote.user_context import UserContext  # noqa: E402
from language_local_python_package.src.language_enum_class import LanguageCode  # noqa: E402


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

user_context = UserContext().login()


class LocationLocal(GenericCRUD):
    def __init__(self):
        logger.start()

        self.connector = Connector.connect("location")
        self.cursor = self.connector.cursor()

        logger.end()

    def get_location_table_ids(
            self, neighborhood: str, county: str, region: str, state: str, country: str) -> tuple(
            [int, int, int, int, int]):
        logger.start(object={"neighborhood": neighborhood, "county": county, "region": region, "state": state, "country": country})

        neighborhood_id, county_id, region_id, state_id, country_id = None, None, None, None, None
        neighborhood_object = Neighborhood()
        neighborhood_id = neighborhood_object.get_neighborhood_id(neighborhood)
        county_object = County()
        county_id = county_object.get_county_id(county)
        region_object = Region()
        region_id = region_object.get_region_id(region)
        state_object = State()
        state_id = state_object.get_state_id(state)
        country_object = Country()
        country_id = country_object.get_country_id(country)

        logger.end(object={"neighborhood_id": neighborhood_id, "county_id": county_id, "region_id": region_id,
                    "state_id": state_id, "country_id": country_id})
        return (neighborhood_id, county_id, region_id, state_id, country_id)

    def insert(
            self, location_data: Dict[str, any],
            lang_code: LanguageCode = "en", is_approved: bool = False, new_country_data: Dict[str, any] = None):
        logger.start(object={"location_data": location_data, "lang_code": lang_code, "is_approved": is_approved, "new_country_data": new_country_data})
        (neighborhood_id, county_id, region_id, state_id, country_id) = self._check_details_and_insert_if_not_exist(
            location_data["coordinate"],
            (location_data["neighborhood"],
             location_data["county"],
             location_data["region"],
             location_data["state"],
             location_data["country"]),
            lang_code, is_approved, new_country_data)

        query_insert = "INSERT INTO location_table (coordinate, address_local_language, address_english," \
            " neighborhood_id, county_id, region_id, state_id, country_id, postal_code, plus_code, is_approved)" \
            " VALUES (POINT(%s, %s), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
        self.cursor.execute(
            query_insert,
            (location_data["coordinate"]["latitude"],
             location_data["coordinate"]["longitude"],
             location_data["address_local_language"],
             location_data["address_english"],
             neighborhood_id, county_id, region_id, state_id, country_id, location_data["postal_code"],
             location_data["plus_code"],
             is_approved))
        self.connector.commit()

        location_id = self.cursor.lastrowid()

        logger.end(object={"location_id": location_id})
        return location_id

    def update(self, location_id: int, location_data: Dict[str, any],
               lang_code: LanguageCode = "en", is_approved: bool = False):
        logger.start(object={"location_id": location_id, "location_data": location_data, "lang_code": lang_code, "is_approved": is_approved})
        (neighborhood_id, county_id, region_id, state_id, country_id) = self._check_details_and_insert_if_not_exist(
            location_data["coordinate"],
            (location_data["neighborhood"],
             location_data["county"],
             location_data["region"],
             location_data["state"],
             location_data["country"]),
            lang_code, is_approved)

        query_update = "UPDATE location_table SET coordinate = POINT(%s, %s), address_local_language = %s, " \
            "address_english = %s, neighborhood_id = %s, county_id = %s, region_id = %s, state_id = %s," \
            " country_id = %s, postal_code = %s, plus_code = %s, is_approved = %s WHERE location_id = %s"
        self.cursor.execute(
            query_update,
            (location_data["coordinate"]["latitude"],
             location_data["coordinate"]["longitude"],
             location_data["address_local_language"],
             location_data["address_english"],
             neighborhood_id, county_id, region_id, state_id, country_id, location_data["postal_code"],
             location_data["plus_code"],
             is_approved,
             location_id))
        self.connector.commit()
        logger.end(object={"location_id": location_id})

    def read(self, location_id: int) -> tuple([Dict[str, float], str, str, str, str, str, str, str, str, str]):
        logger.start(object={"location_id": location_id})

        query_get = '''SELECT ST_X(l.coordinate), ST_Y(l.coordinate), l.address_local_language, l.address_english, n.title, c.title, 
                            r.title, s.state_name, co.name, l.postal_code, l.plus_code 
                            FROM location_view l 
                            JOIN neighborhood_ml_view n ON n.neighborhood_id = l.neighborhood_id
                            JOIN county_ml_view c ON c.county_id = l.county_id
                            JOIN region_ml_view r ON r.region_id = l.region_id
                            JOIN state_ml_view s ON s.state_id = l.state_id 
                            JOIN country_table co ON co.country_id = l.country_id 
                            WHERE l.location_id = %s'''
        self.cursor.execute(query_get, (location_id,))
        result = None
        coordinate = None
        result = self.cursor.fetchone()
        if result is None:
            logger.end(object={"result": result})
            return None

        (latitude, longitude, address_local_language, address_english, neighborhood,
         county, region, state, country, postal_code, plus_code) = result
        coordinate = {"latitude": latitude, "longitude": longitude}

        logger.end(object={"address_local_language": address_local_language, "address_english": address_english,
                    "neighborhood": neighborhood, "county": county, "region": region, "state": state,
                    "country": country, "postal_code": postal_code, "plus_code": plus_code})
        return (coordinate, address_local_language, address_english, neighborhood, county, region,
                state, country, postal_code, plus_code)

    def delete(self, location_id: int):
        logger.start(object={"location_id": location_id})

        query_update = "UPDATE location_table SET end_timestamp = NOW() WHERE location_id = %s"
        self.cursor.execute(query_update, (location_id,))

        self.connector.commit()
        logger.end()

    def _check_details_and_insert_if_not_exist(
            self, coordinate: Dict[str, float],
            location_details: tuple([str, str, str, str, str]),
            lang_code: LanguageCode = 'en', is_approved=False, new_country_data: Dict[str, any] = None) -> tuple(
            [int, int, int, int, int]):
        logger.start()
        (neighborhood, county, region, state, country) = location_details
        ids = self.get_location_table_ids(
            neighborhood, county, region, state, country)
        if ids is None:
            return None
        (neighborhood_id, county_id, region_id, state_id, country_id) = ids
        if neighborhood_id is None:
            neighborhood_object = Neighborhood()
            neighborhood_id = neighborhood_object.insert_neighborhood(
                coordinate, neighborhood, lang_code, )
        if county_id is None:
            county_object = County()
            county_id = county_object.insert_county(coordinate, county, lang_code, is_approved)
        if region_id is None:
            region_object = Region()
            region_id = region_object.insert_region(coordinate, region, lang_code, is_approved)
        if state_id is None:
            state_object = State()
            state_id = state_object.insert_state(coordinate, state, lang_code, is_approved)
        if country_id is None:
            country_object = Country()
            country_id = country_object.insert_country(coordinate, country, lang_code, is_approved, new_country_data)
        logger.end(object={"neighborhood_id": neighborhood_id, "county_id": county_id, "region_id": region_id, "state_id": state_id, "country_id": country_id})
        return (neighborhood_id, county_id, region_id, state_id, country_id)
