import time
from datetime import datetime, timedelta, timezone

from sqlalchemy import text
from sqlalchemy.orm import declarative_base, declared_attr
from sqlalchemy.schema import Column
from sqlalchemy.sql.functions import now
from sqlalchemy.sql.sqltypes import Integer, DateTime

from files.helpers.time import format_age, format_datetime

Base = declarative_base()


class CreatedBase(Base):
	__abstract__ = True
	
	def __init__(self, *args, **kwargs):
		if "created_utc" not in kwargs: 
			kwargs["created_utc"] = int(time.time())
		super().__init__(*args, **kwargs)
	
	@declared_attr
	def created_utc(self):
		return Column(Integer, nullable=False)
	
	@property
	def created_date(self) -> str:
		return self.created_datetime
	
	@property
	def created_datetime(self) -> str:
		return format_datetime(self.created_utc)
	
	@property
	def created_datetime_py(self) -> datetime:
		return datetime.fromtimestamp(self.created_utc, tz=timezone.utc)
	
	@property
	def age_seconds(self) -> int:
		return time.time() - self.created_utc
	
	@property
	def age_timedelta(self) -> timedelta:
		return datetime.now(tz=timezone.utc) - self.created_datetime_py

	@property
	def age_string(self) -> str:
		return format_age(self.created_utc)


class CreatedDateTimeBase(Base):
	"""
	An abstract class extending our default SQLAlchemy's `Base`.

	All classes inherit from this class automatically maps a `created_datetimez` column
	for the corresponding SQL table. This column will automatically record the created
	timestamp of rows. Retrieving `created_datetimez` will return a `datetime` object with 
	`tzinfo` of UTC.

	This class holds various convenience properties to get `created_datetimez` in different
	formats.
	"""
	__abstract__ = True

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
	
	@declared_attr
	def created_datetimez(self):
		"""
		Declare default column for classes/tables inheriting `CreatedDateTimeBase`.
		
		Retrieving `created_datetimez` will return a `datetime` object with `tzinfo` for UTC.

		New rows have a default created_datetimez of the database server's `now()`
		"""
		return Column(DateTime(timezone=True), nullable=False, server_default=now())

	@property
	def created_utc(self):
		"""
		the created date in UTC seconds. Milliseconds are truncated/rounded down.
		"""
		return int(self.created_datetimez.timestamp())
	
	@property
	def created_date(self) -> str:
		"""
		the created date in string.
		See `file.helpers.time.DATETIME_FORMAT` for the exact format
		Note: should this be using `format_date` and not `format_datetime`?
		"""
		return self.created_datetime
	
	@property
	def created_datetime(self) -> str:
		"""
		the created datetime in string.
		See `file.helpers.time.DATETIME_FORMAT` for the exact format.
		"""
		return format_datetime(self.created_datetimez)
	
	@property
	def created_datetime_py(self) -> datetime:
		"""
		the created datetime as a `datetime` object with `tzinfo` of UTC.
		"""
		return self.created_datetimez
	
	@property
	def age_seconds(self) -> int:
		"""
		number of seconds since created.
		"""
		return time.time() - self.created_utc
	
	@property
	def age_timedelta(self) -> timedelta:
		"""
		a `timedelta` object representing time since created.
		"""
		return datetime.now(tz=timezone.utc) - self.created_datetimez

	@property
	def age_string(self) -> str:
		"""
		a string representing time since created. Example: "1h ago", "2d ago".
		"""
		return format_age(self.created_datetimez)
