import requests
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.base import ContentFile
from django.db.models import Q

from apps.account.models import User
from apps.master.models import ConnectorMaster, NetworkOperatorMaster
from apps.station import models as station_model
from apps.station.models import StationMedia
from base.constants import AssetTypeConstants, FileFieldConstants
from station_ev import settings


def get_station_ids_by_connector_ids(connector_ids):
    """
    Retrieve station IDs associated with a list of connector IDs.

    Args:
        connector_ids (list): A list of connector IDs to query.

    Returns:
        list: A list of station IDs that have the specified connector IDs.
    """
    if len(connector_ids) == 0:
        return []

    connector_ids = [
        int(connector_id) for connector_id in connector_ids if connector_id.isdigit()
    ]
    existing_station_ids = station_model.StationConnectors.objects.filter(
        connector__id__in=connector_ids
    ).values_list("station__id", flat=True)
    return existing_station_ids


def get_station_ids_by_connector_kilowatts(min_kilowatts, max_kilowatts):
    """
    Retrieve station IDs associated with connectors within a specified kilowatt range.

    Args:
        min_kilowatts (float): The minimum kilowatt value.
        max_kilowatts (float): The maximum kilowatt value.

    Returns:
        list: A list of station IDs that have connectors with kilowatt values within the specified range.
    """

    existing_station_ids = station_model.StationConnectors.objects.filter(
        Q(kilowatts__gte=min_kilowatts) & Q(kilowatts__lte=max_kilowatts)
    ).values_list("station__id", flat=True)
    return existing_station_ids


def get_station_ids_by_location_ids(location_ids):
    """
    Retrieve station IDs associated with a list of location IDs.

    Args:
        location_ids (list): A list of location IDs to query.

    Returns:
        list: A list of station IDs that have the specified location IDs.
    """
    if len(location_ids) == 0:
        return []

    location_ids = [
        int(location_id) for location_id in location_ids if location_id.isdigit()
    ]
    existing_station_ids = station_model.StationLocation.objects.filter(
        location__id__in=location_ids
    ).values_list("station__id", flat=True)

    return existing_station_ids


def get_station_ids_by_network_operator_ids(network_operator_ids):
    """
    Retrieve station IDs associated with a list of network operator IDs.

    Args:
        network_operator_ids (list): A list of network operator IDs to query.

    Returns:
        list: A list of station IDs that have the specified network operator IDs.
    """

    # Check if the list of network operator IDs is empty
    if len(network_operator_ids) == 0:
        return []

    # Convert the network operator IDs to integers
    network_operator_ids = [
        int(network_operator_id)
        for network_operator_id in network_operator_ids
        if network_operator_id.isdigit()
    ]

    # Query the database to retrieve station IDs associated with the specified network operator IDs
    existing_station_ids = station_model.StationConnectors.objects.filter(
        network_operator__id__in=network_operator_ids
    ).values_list("station__id", flat=True)

    return existing_station_ids


def get_station_ids_by_amenity_ids(amenity_ids):
    """
    Retrieve station IDs associated with a list of amenity IDs.

    Args:
        amenity_ids (list): A list of amenity IDs to query.

    Returns:
        list: A list of station IDs that have the specified amenity IDs.
    """

    if len(amenity_ids) == 0:
        return []
    amenity_ids = [
        int(amenity_id) for amenity_id in amenity_ids if amenity_id.isdigit()
    ]
    existing_station_ids = station_model.StationAmenities.objects.filter(
        amenities__id__in=amenity_ids
    ).values_list("station__id", flat=True)
    return existing_station_ids


def get_station_by_id(station_id):
    """
    Retrieve a station record by its ID.

    Args:
        station_id (int): The ID of the station to retrieve.

    Returns:
        StationMaster: The station record corresponding to the provided station ID, or None if it does not exist.
    """
    try:
        return station_model.StationMaster.objects.get(id=station_id)
    except ObjectDoesNotExist:
        return None


def handle_stations(scarp_id=None, validated_data=None):
    """
    Create or update a station record based on scrap ID and validated data.

    Args:
        scarp_id (str): The scrap ID to identify the station.
        validated_data (dict): Validated data containing station details.

    Returns:
        StationMaster: The created or updated station record.

    This function creates or updates a station record based on the scrap ID. If a station with the given scrap ID exists,
    it updates the existing record with the provided data. If not, it creates a new station record.
    """
    if scarp_id:
        station, created = station_model.StationMaster.objects.get_or_create(
            scarp_id=scarp_id, defaults=validated_data
        )
        if not created:
            # Update the existing station with new data
            for key, value in validated_data.items():
                setattr(station, key, value)
            station.save()
    else:
        station = station_model.StationMaster.objects.create(**validated_data)

    return station


def handle_stations_user(station, user):
    """
    Create or retrieve a station user association.

    Args:
        station (StationMaster): The station record to associate with the user.
        user (User): The user to associate with the station.

    Returns:
        StationUser: The created or retrieved station user association.

    This function creates or retrieves a station user association, linking a station with a user.
    """

    station_user, created = station_model.StationUser.objects.get_or_create(
        station=station, user=user
    )
    return station_user


def handle_locations(station, new_locations):
    """
    Handle amenities for a station, updating or creating StationAmenities records.

    Args:
        station (StationMaster): The station for which amenities are being managed.
        new_locations (list of LocationMaster): The list of new locations to be associated with the station.

    Returns:
        None
    """
    # Get the existing amenity IDs for the station
    existing_location_ids = station_model.StationLocation.objects.filter(
        station=station
    ).values_list("location__id", flat=True)

    # Create a list of IDs for the new amenities
    location_ids = [location.id for location in new_locations]

    # Find the IDs that are in the existing data but not in the new data
    locations_to_delete = set(existing_location_ids) - set(location_ids)

    # Delete the unused StationAmenities records
    station_model.StationLocation.objects.filter(
        station=station, location__id__in=locations_to_delete
    ).delete()

    # Create or update StationAmenities records for the new data
    for location in new_locations:
        try:
            station_model.StationLocation.objects.get_or_create(
                station=station, location=location
            )
        except (ObjectDoesNotExist, ValueError):
            pass


def handle_amenities(station, new_amenities):
    """
    Handle amenities for a station, updating or creating StationAmenities records.

    Args:
        station (StationMaster): The station for which amenities are being managed.
        new_amenities (list of AmenitiesMaster): The list of new amenities to be associated with the station.

    Returns:
        None
    """
    # Get the existing amenity IDs for the station
    existing_amenity_ids = station_model.StationAmenities.objects.filter(
        station=station
    ).values_list("amenities__id", flat=True)

    # Create a list of IDs for the new amenities
    amenity_ids = [amenity.id for amenity in new_amenities]

    # Find the IDs that are in the existing data but not in the new data
    amenity_to_delete = set(existing_amenity_ids) - set(amenity_ids)

    # Delete the unused StationAmenities records
    station_model.StationAmenities.objects.filter(
        station=station, amenities__id__in=amenity_to_delete
    ).delete()
    # Create or update StationAmenities records for the new data
    for amenity in new_amenities:
        station_model.StationAmenities.objects.get_or_create(
            station=station, amenities=amenity
        )


def handle_update_connectors(station, new_connectors):
    """
    Handle connectors for a station, updating StationConnectors records.

    Args:
        station (StationMaster): The station for which connectors are being managed.
        new_connectors (list of dict): The list of new connectors data to be associated with the station.
            Each item in the list is a dictionary with keys 'connector', 'kilowatts', and 'make' for the connector data.

    Returns:
        None
    """

    # Remove exists StationConnectors
    station_model.StationConnectors.objects.filter(station=station).delete()

    # Iterate through the list of new connectors data
    for connector_data in new_connectors:
        # Create or update StationConnectors records for the new connector data
        station_model.StationConnectors.objects.create(
            station=station,
            connector=connector_data.get("connector"),
            kilowatts=connector_data.get("kilowatts"),
            network_operator=connector_data.get("network_operator"),
        )


def handle_connectors(station, new_connectors):
    """
    Handle connectors for a station, updating or creating StationConnectors records.

    Args:
        station (StationMaster): The station for which connectors are being managed.
        new_connectors (list of dict): The list of new connectors data to be associated with the station.
            Each item in the list is a dictionary with keys 'connector', 'kilowatts', and 'make' for the connector data.

    Returns:
        None
    """

    # Iterate through the list of new connectors data
    for connector_data in new_connectors:
        for outlet in connector_data["outlets"]:
            # Create or update StationConnectors records for the new connector data
            station_model.StationConnectors.objects.create(
                station=station,
                connector=outlet["connector"],
                kilowatts=outlet["kilowatts"],
                network_operator=connector_data["make"],
            )


def get_or_create_network_operator(network_operator_data):
    """
    Get or create a network operator based on the provided data.

    Args:
        network_operator_data (dict): Data for the network operator,
        including 'id', 'name', 'phone', 'url', and 'action_url'.

    Returns:
        NetworkOperatorMaster: The created or existing network operator instance.
    """
    network_id = network_operator_data.get("id")
    name = network_operator_data.get("name")
    phone = network_operator_data.get("phone")
    url = network_operator_data.get("url")
    action_url = network_operator_data.get("action_url")

    if network_id and name:
        defaults = {"name": name}
        if phone:
            defaults["phone"] = phone
        if url or action_url:
            defaults["url"] = url or action_url

        network_operator, create = NetworkOperatorMaster.objects.get_or_create(
            type_id=network_id, defaults=defaults
        )
        return network_operator
    else:
        return None


def get_or_create_connector(connector_data):
    """
    Get or create a connector based on the provided data.

    Args:
        connector_data (dict): Data for the connector, including 'connector' and 'connector_name'.

    Returns:
        ConnectorMaster: The created or existing connector instance.
    """
    connector_id = connector_data.get("connector")
    connector_name = connector_data.get("connector_name")

    if connector_id and connector_name:
        connector, create = ConnectorMaster.objects.get_or_create(
            type_id=connector_id,
            defaults={"name": connector_name, "short_name": connector_name},
        )
        return connector
    else:
        return None


def get_suggest_station(suggest_id):
    """
    Get or create a connector based on the provided data.

    Args:
        suggest_id (int): id of SuggestStation tabel

    Returns:
        ConnectorMaster: To get existing SuggestStation instance.
    """

    try:
        return station_model.SuggestStation.objects.get(id=suggest_id)
    except ObjectDoesNotExist:
        return None


def handle_station_media(station, user, station_photos):
    if station_photos:
        try:
            # Get count from ENV
            extract_image_count = settings.EXTRACT_STATION_IMAGE_COUNT

            # check count grater-then current station_photos list then use only extract_image_count
            if extract_image_count > len(station_photos):
                station_photos = station_photos[:extract_image_count]

            # Extract the first photo URL
            for photo in station_photos:
                first_photo_url = photo.get("url")
                extension = first_photo_url.split(".")[-1].lower()

                # Check if the extension is a valid image format
                if extension in FileFieldConstants.IMAGE_FORMATS:
                    response = requests.get(first_photo_url)

                    if response.status_code == 200:
                        content = response.content

                        # Create a ContentFile from the downloaded content
                        content_file = ContentFile(
                            content, name=first_photo_url.split(".")[-1]
                        )

                        # Get user
                        user = User.objects.get(pk=user)

                        # Create and save the StationMedia instance for the first photo
                        station_media = StationMedia.objects.create(
                            station=station,
                            asset=content_file,
                            asset_type=AssetTypeConstants.IMAGE,
                            user=user,
                            is_verified=True,
                        )
                        station_media.save()

        except Exception as e:
            # Handle exceptions appropriately (logging, reporting, etc.)
            pass
