from setup import DATABASE, CONFIG, DB_SESSION
from sqlalchemy import Column, String, Float, Integer, ARRAY, TIMESTAMP, Boolean, ForeignKey, text
from sqlalchemy.orm import relationship
from ...services.custom_sqlalchemy_types import UtcNow, EnumSqlalchemyType, StringEnumSqlalchemyType
from ...services.satbots.satbot_type import SatbotType
from ...services.satbots.satbot_state import SatbotState
from ...services.satbots.satbot_priority import SatbotPriority
import os
from sqlalchemy.dialects.postgresql import JSON

from datetime import datetime
from main.services.enums.user import EventCategory
from main.services.event_store import EventStore

class Satbot(DATABASE.Model):
    __tablename__ = 'satbots'
    __bind_key__ = 'satbots'

    id              = Column("id", Integer, primary_key=True)
    type            = Column("type", EnumSqlalchemyType(SatbotType), nullable=False)
    parameters      = Column("parameters", JSON, nullable=False)
    state           = Column("state", EnumSqlalchemyType(SatbotState), nullable=False)
    progress        = Column("progress", Integer)
    pid             = Column("pid", Integer)
    priority        = Column("priority", EnumSqlalchemyType(SatbotPriority))
    log             = Column("log", ARRAY(String))
    created_at      = Column("created_at", TIMESTAMP(timezone='UTC'), server_default=UtcNow(), nullable=False)
    created_by_id   = Column("created_by_id", Integer)
    started_at      = Column("started_at", TIMESTAMP(timezone='UTC'))
    finished_at     = Column("finished_at", TIMESTAMP(timezone='UTC'))

    @property
    def to_dict_minimal(self):
        return {
            "id": self.id,
            "type": {'primary_fields': self.type.primary_fields, 'title': self.type.title, 'downloadable': self.type.downloadable },
            "state": {'name': self.state.name, 'title': self.state.title },
            "parameters": self.parameters,
            "priority": self.priority.to_dict,
            "progress": self.progress,
            "created_at": self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
            "finished_at": self.finished_at.strftime("%Y-%m-%d %H:%M:%S") if self.finished_at else ''
        }

    @property
    def to_dict(self):
        created_by = self.created_by
        return self.to_dict_minimal | {
            "pid": self.pid,
            "created_by": created_by.to_dict_name if created_by else None,
            "started_at": self.started_at.strftime("%Y-%m-%d %H:%M:%S") if self.started_at else '',
            "time_taken": self.time_taken or '',
            "log": self.log,
            "cannot_be_urgent": self.type.cannot_be_urgent
        }

    @property
    def time_taken(self):
        if not self.started_at:
            return None

        end_time = self.finished_at
        if not end_time:
            end_time = datetime.utcnow()

        delta = end_time - self.started_at

        output = {}
        output['days'], remaining = divmod(delta.total_seconds(), 86_400)
        output['hours'], remaining = divmod(remaining, 3_600)
        output['minutes'], output['seconds'] = divmod(remaining, 60)

        output = [f'{round(value)} {name[:-1] if round(value) == 1 else name}' for name, value in output.items() if value > 0]
        output = ', '.join(output)
        return output + '...' if not self.finished_at else output

    # Expects to be run within app.app_context()
    def update(self, state=None, log=None, progress=None):
        if state:
            self.state = state
            timestamp = datetime.utcnow()
            body = f"Satbot {self.id} ({self.type.title}) {self.state.emoji} {self.state}"

            if state.value == SatbotState.STARTED.value:
                self.pid = os.getpid()
                self.started_at = timestamp

            if state.value in [SatbotState.FINISHED.value, SatbotState.ERROR.value]:
                self.finished_at = timestamp
                body += f" after {self.time_taken}"

            EventStore(
                user_agent='API',
                user_id=self.created_by_id,
                timestamp=timestamp,
                category=EventCategory.SATBOT,
                private_data={
                    "id": self.id
                },
                public_data={
                    "message": body
                }
            )

        if log:
            self.log = (self.log or []) + log

        if progress:
            self.progress=progress

        self.query.session.add(self)
        self.query.session.commit()

    @property
    def working_directory(self):
        directory = f"{CONFIG['satbots']['working_directory']}/satbot{self.id}"
        os.makedirs(directory, exist_ok = True)
        return directory

    @property
    def download_directory(self):
        if self.type.downloadable:
            if self.type == SatbotType.GENERATE_DATA:
                if self.parameters.get('order_id'):
                    return f"{CONFIG['order_download_base_directory']}"
                return f"{CONFIG['download_base_directory']}"

            if self.type in [SatbotType.EXPORT_DATA, SatbotType.DATABASE_BACKUP]:
                return CONFIG['satbots']['backups_directory']
        return None

    @property
    def file_name(self):
        if self.type == SatbotType.EXPORT_DATA:
            category = 'disps' if self.parameters.get('include-disps') else 'points'
            return f"region-{self.parameters.get('region')}-{category}-{self.created_at.strftime('%Y-%m-%d')}.zip"

        if self.type == SatbotType.DATABASE_BACKUP:
            return f"{self.created_at.strftime('%Y-%m-%d--%H-%M-%S')}-{self.parameters.get('--database')}.zip"

        return f"satbot{self.id}-data.zip"
    
    @property
    def created_by(self):
        from main.services.users_helper import find_user

        return find_user(self.created_by_id)
