from django.contrib.gis.db.models import PointField
from django.contrib.postgres.indexes import GistIndex
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.text import slugify

from apps.station.managers import station_manager
from base.constants import AssetTypeConstants, StationConstants
from base.documents_path import DocumentsPath
from base.models import BaseModel


class StationMaster(BaseModel):
    """
    This is the StationMaster model.
    It represents system stations.

    Attributes:
        name (str): The name of the station.
        address (str): The address of the station.
        description (str): A description of the station.
        phone_number (str): The phone number of the station.
        email (str): The email address of the station.
        website (str): The website URL of the station.
        is_active (bool): Indicates whether the station is active.
        open_time (TimeField): The opening time of the station (nullable).
        close_time (TimeField): The closing time of the station (nullable).
        is_24_hours_open (bool): Indicates if the station is open 24 hours.
        is_parking_free (bool): Indicates if parking at the station is free.
        coordinates (PointField): The geographic coordinates of the station.
        is_owner_or_user (bool): Indicates if the user is the owner or a regular user of the station.
        scarp_id (BigIntegerField): An identifier for the station.

    Meta:
        db_table (str): The database table name for the StationMaster model.

    Methods:
        __str__(): Returns a string representation of the name.
    """

    name = models.CharField(null=True, blank=True)
    address = models.CharField(null=True, blank=True)
    description = models.CharField(null=True, blank=True)
    title_description = models.TextField(null=True, blank=True)
    meta_description = models.TextField(null=True, blank=True)
    phone_number = models.CharField(null=True, blank=True, max_length=20)
    email = models.EmailField(null=True, blank=True)
    website = models.URLField(null=True, blank=True)
    is_active = models.BooleanField(null=True, blank=True)
    open_time = models.TimeField(null=True)
    close_time = models.TimeField(null=True)
    is_24_hours_open = models.BooleanField(null=True, blank=True)
    is_parking_free = models.BooleanField(null=True, blank=True)
    parking_cost_description = models.CharField(null=True, blank=True)
    coordinates = PointField(null=False, blank=False)
    is_owner_or_user = models.IntegerField(
        choices=StationConstants.get_choices(), null=True
    )
    scarp_id = models.BigIntegerField(null=True)
    is_verified = models.BooleanField(default=False)
    slug = models.SlugField(unique=True, default=None, null=True, max_length=255)

    objects = station_manager.StationManager()

    class Meta:
        db_table = "station_master"
        indexes = [
            models.Index(fields=["name"]),  # Index on the 'name' field
            models.Index(fields=["is_verified"]),  # Index on the 'is_verified' field
            models.Index(
                fields=["is_24_hours_open"]
            ),  # Index on the 'is_24_hours_open' field
            GistIndex(fields=["coordinates"]),
        ]

    def __str__(self):
        return f"{self.name}"


@receiver(post_save, sender=StationMaster)
def post_save_station_master(sender, instance, created, **kwargs):
    # Only update slug if the instance is newly created
    if created:
        instance.slug = slugify(f"{instance.name}-{instance.pk}")
        # Save only the slug field to avoid recursion
        instance.save(update_fields=["slug"])


class StationUser(BaseModel):
    """
    This is the StationUser model.
    It represents the association of a user with a station.

    Attributes:
        user (ForeignKey): Relates the user to a station.
        station (ForeignKey): Relates the station to a user.

    Meta:
        db_table (str): The database table name for the StationUser model.
    """

    user = models.ForeignKey(
        "account.user",
        on_delete=models.CASCADE,
        related_name="user_stations",
        null=True,
    )
    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_users"
    )

    objects = station_manager.StationUsersManager()

    class Meta:
        db_table = "station_user"


class StationCountryStateCity(BaseModel):
    """
    This is the StationCountryStateCity model.
    It represents the association of a station with a country, state, and city.

    Attributes:
        country (ForeignKey): Relates the station to a country.
        state (ForeignKey): Relates the station to a state.
        city (ForeignKey): Relates the station to a city.
        station (ForeignKey): Relates the station to a main station.

    Meta:
        db_table (str): The database table name for the StationCountryStateCity model.
    """

    country = models.ForeignKey(
        "master.CountryMaster",
        on_delete=models.CASCADE,
        related_name="country_stations",
        null=True,
    )
    state = models.ForeignKey(
        "master.StateMaster",
        on_delete=models.CASCADE,
        related_name="state_stations",
        null=True,
    )
    city = models.ForeignKey(
        "master.CityMaster",
        on_delete=models.CASCADE,
        related_name="city_stations",
        null=True,
    )
    station = models.ForeignKey(
        StationMaster,
        on_delete=models.CASCADE,
        related_name="station_city_state_country",
    )

    class Meta:
        db_table = "station_country_state_city"


class StationMedia(BaseModel):
    """
    This is the StationMedia model.
    It represents media associated with a station.

    Attributes:
        asset (FileField): The uploaded media file associated with the station.
        asset_type (int): The type of the media (e.g., image, video, audio).
        station (ForeignKey): Relates the media to a station.
        user (ForeignKey): Relates the media to a user who uploaded it.

    Meta:
        db_table (str): The database table name for the StationMedia model.
    """

    asset = models.FileField(upload_to=DocumentsPath.get_station_path)
    asset_type = models.IntegerField(choices=AssetTypeConstants.get_asset_choices())
    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_medias"
    )
    user = models.ForeignKey(
        "account.User", on_delete=models.CASCADE, related_name="user_medias"
    )
    is_verified = models.BooleanField(default=False)

    objects = station_manager.StationMediaManager()

    class Meta:
        db_table = "station_media"


class StationLocation(BaseModel):
    """
    This is the StationLocation model.
    It represents the location of a station.

    Attributes:
        station (ForeignKey): Relates the location to a station.
        location (ForeignKey): Relates the location to a specific geographical location.

    Meta:
        db_table (str): The database table name for the StationLocation model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_locations"
    )
    location = models.ForeignKey(
        "master.LocationMaster",
        on_delete=models.CASCADE,
        related_name="location_stations",
    )

    objects = station_manager.StationLocationsManager()

    class Meta:
        db_table = "station_locations"


class StationConnectors(BaseModel):
    """
    This is the StationConnectors model.
    It represents the availability of connectors in a station.

    Attributes:
        station (ForeignKey): Relates the connectors to a station.
        connector (ForeignKey): Relates the connectors to a specific connector type.
        network_operator (ForeignKey): Relates the connectors to a network operator.

    Meta:
        db_table (str): The database table name for the StationConnectors model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_connectors"
    )
    connector = models.ForeignKey(
        "master.ConnectorMaster",
        on_delete=models.CASCADE,
        related_name="connector_stations",
        null=True,
    )
    kilowatts = models.FloatField(null=True, blank=True)
    cost_description = models.CharField(null=True, blank=True)
    network_operator = models.ForeignKey(
        "master.NetworkOperatorMaster",
        on_delete=models.CASCADE,
        related_name="network_operator_stations",
        null=True,
    )

    objects = station_manager.StationConnectorsManager()

    class Meta:
        db_table = "station_connectors"


class StationAmenities(BaseModel):
    """
    This is the StationAmenities model.
    It represents amenities associated with a station.

    Attributes:
        station (ForeignKey): Relates the amenities to a station.
        amenities (ForeignKey): Relates the amenities to a specific type of amenity.

    Meta:
        db_table (str): The database table name for the StationAmenities model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_amenities"
    )
    amenities = models.ForeignKey(
        "master.AmenitiesMaster",
        on_delete=models.CASCADE,
        related_name="amenities_stations",
    )

    objects = station_manager.StationAmenitiesManager()

    class Meta:
        db_table = "station_amenities"


class BookmarkStation(BaseModel):
    """
    This is the BookmarkStation model.
    It represents stations that are bookmarked by users.

    Attributes:
        station (ForeignKey): Relates the bookmark to a station.
        user (ForeignKey): Relates the bookmark to a user who added the bookmark.

    Meta:
        db_table (str): The database table name for the BookmarkStation model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_bookmarks"
    )
    user = models.ForeignKey(
        "account.User", on_delete=models.CASCADE, related_name="user_bookmark_stations"
    )

    objects = station_manager.StationBookmarkManager()

    class Meta:
        db_table = "bookmark_station"


class SuggestStation(BaseModel):
    """
    This is the SuggestStation model.
    It represents suggestions of stations sent by end users.

    Attributes:
        station (ForeignKey): Relates the suggestion to a station.
        user (ForeignKey): Relates the suggestion to the user who made the suggestion.
        description (TextField, optional): Additional description for the suggestion.

    Meta:
        db_table (str): The database table name for the SuggestStation model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_suggest_stations"
    )
    user = models.ForeignKey(
        "account.User", on_delete=models.CASCADE, related_name="user_suggest_stations"
    )
    description = models.TextField(null=True, blank=True)
    status = models.IntegerField(
        choices=StationConstants.get_suggest_station_request_status(),
        default=StationConstants.PENDING,
    )

    objects = station_manager.SuggestStationManager()

    class Meta:
        db_table = "suggest_station"


class SuggestStationMedia(BaseModel):
    """
    This is the SuggestStationMedia model.
    It represents media related to suggested stations.

    Attributes:
        asset (FileField): The uploaded media asset.
        asset_type (IntegerField): The type of asset.
        suggest_station (ForeignKey): Relates the media to a suggested station.
        user (ForeignKey): Relates the media to the user who uploaded it.

    Meta:
        db_table (str): The database table name for the SuggestStationMedia model.
    """

    asset = models.FileField(upload_to=DocumentsPath.get_suggest_station_path)
    asset_type = models.IntegerField(choices=AssetTypeConstants.get_asset_choices())
    suggest_station = models.ForeignKey(
        SuggestStation, on_delete=models.CASCADE, related_name="suggest_station_medias"
    )
    user = models.ForeignKey(
        "account.User",
        on_delete=models.CASCADE,
        related_name="user_suggest_station_medias",
    )

    objects = station_manager.SuggestStationMediaManager()

    class Meta:
        db_table = "suggest_station_media"


class StationReasonMaster(BaseModel):
    """
    This is the StationReasonMaster model.
    It represents reasons for reporting stations.

    Attributes:
        title (CharField): The title of the reason.

    Meta:
        db_table (str): The database table name for the StationReasonMaster model.
    """

    title = models.CharField(blank=False, null=False)

    objects = station_manager.StationReportReasonManager()

    class Meta:
        db_table = "station_reason_master"


class StationReport(BaseModel):
    """
    This is the StationReport model.
    It represents reports for stations.

    Attributes:
        reason (ForeignKey): Relates the report to a reason for reporting.
        station (ForeignKey): Relates the report to a station.
        user (ForeignKey): Relates the report to the user who made the report.
        description (TextField, optional): Additional description for the report.

    Meta:
        db_table (str): The database table name for the StationReport model.
    """

    reason = models.ForeignKey(
        StationReasonMaster,
        on_delete=models.CASCADE,
        related_name="reason_station_reports",
        null=True,
    )
    station = models.ForeignKey(
        StationMaster,
        on_delete=models.CASCADE,
        related_name="station_reports",
    )
    user = models.ForeignKey(
        "account.User", on_delete=models.CASCADE, related_name="user_station_reports"
    )
    description = models.TextField(blank=True, null=True)

    objects = station_manager.StationReportManager()

    class Meta:
        db_table = "station_report"


class StationCheckIn(BaseModel):
    """
    This is the StationCheckIn model.
    It represents checkins for stations.

    Attributes:
        station (ForeignKey): Relates the rating to a station.
        user (ForeignKey): Relates the rating to the user who provided the checkIn.
    Meta:
        db_table (str): The database table name for the StationCheckIn model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_checkin"
    )
    user = models.ForeignKey(
        "account.User",
        on_delete=models.SET_NULL,
        related_name="user_station_checkins",
        null=True,
    )
    user_vehicle = models.ForeignKey(
        "account.UserVehicle",
        on_delete=models.SET_NULL,
        related_name="user_vehicles_checkins",
        null=True,
    )

    objects = station_manager.StationCheckInManager()

    class Meta:
        db_table = "station_checkin"


class StationRating(BaseModel):
    """
    This is the StationRating model.
    It represents ratings for stations.

    Attributes:
        station (ForeignKey): Relates the rating to a station.
        user (ForeignKey): Relates the rating to the user who provided the rating.
        rating (FloatField): The numeric rating value.
        label (ForeignKey): Relates the rating to a label for the rating.
        description (TextField, optional): Additional description for the rating.

    Meta:
        db_table (str): The database table name for the StationRating model.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_ratings"
    )
    user = models.ForeignKey(
        "account.User", on_delete=models.CASCADE, related_name="user_station_ratings"
    )
    rating = models.PositiveIntegerField(
        default=0, validators=[MinValueValidator(1), MaxValueValidator(5)]
    )
    label = models.ForeignKey(
        "master.RatingLabelMaster",
        on_delete=models.SET_NULL,
        related_name="label_station_ratings",
        null=True,
    )
    description = models.TextField(blank=True, null=True)

    objects = station_manager.StationRatingManager()

    class Meta:
        db_table = "station_rating"


class StationRatingMedia(BaseModel):
    """
    This is the StationRatingMedia model.
    It represents media associated with rating stations.

    Attributes:
        rating_station (ForeignKey): Relates the media to a rating station.
        asset (FileField): The uploaded media asset.
        asset_type (IntegerField): The type of asset.

    Meta:
        db_table (str): The database table name for the StationRatingMedia model.
    """

    rating_station = models.ForeignKey(
        StationRating, on_delete=models.CASCADE, related_name="rating_station_medias"
    )
    asset = models.FileField(upload_to=DocumentsPath.get_station_rating_path)
    asset_type = models.IntegerField(choices=AssetTypeConstants.get_asset_choices())
    is_verified = models.BooleanField(default=False)

    objects = station_manager.StationRatingMediaManager()

    class Meta:
        db_table = "station_rating_media"


class RejectedStations(BaseModel):
    """
    Model representing rejected stations.

    This model contains information about stations that have been rejected,
    including the station, the user who rejected it, and the reason for rejection.
    """

    station = models.ForeignKey(
        StationMaster, on_delete=models.CASCADE, related_name="station_rejected"
    )
    user = models.ForeignKey(
        "account.User",
        on_delete=models.SET_NULL,
        related_name="user_station_rejected",
        null=True,
    )
    reason = models.TextField(blank=True, null=True)

    objects = station_manager.RejectedStationsManager()

    class Meta:
        db_table = "rejected_stations"
