from peewee import Model, CharField, IntegerField, DateField, ForeignKeyField, BooleanField, DateTimeField, TimeField, TextField
from playhouse.migrate import MySQLMigrator, migrate
from playhouse.pool import PooledMySQLDatabase
import logging
import threading
import datetime
import bcrypt

from config import config

# Enable Peewee DEBUG logging
PEELOGGER = logging.getLogger('peewee')
# LOGGER.addHandler(logging.StreamHandler())
# LOGGER.setLevel(logging.DEBUG)

dbconfig = config.mysql

# Custom database class with detailed connection logging
class LoggingPooledMySQLDatabase(PooledMySQLDatabase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._idle_timer = None
        self._timer_lock = threading.Lock()

    def _connect(self, *args, **kwargs):
        PEELOGGER.debug(f"DB CONNECT: Opening new connection to {self.database}")
        connection = super()._connect(*args, **kwargs)
        PEELOGGER.debug(f"DB CONNECT: Successfully connected to {self.database}")

        if self._idle_timer:
            with self._timer_lock:
                self._idle_timer.cancel()
                del self._idle_timer
                self._idle_timer = None
                PEELOGGER.debug("DB TIMER: Stopped idle timer")
        return connection

    def _close(self, conn, close_conn=False):
        PEELOGGER.debug(f"DB CLOSE: Closing connection to {self.database} (close_conn={close_conn})")
        super()._close(conn, close_conn)

        # Start idle timer when connection is returned to pool
        if not close_conn:
            with self._timer_lock:
                # Cancel existing timer if any
                if self._idle_timer:
                    self._idle_timer.cancel()

                # Start new timer
                def close_idle_connection():
                    with self._timer_lock:
                        del self._idle_timer
                        self._idle_timer = None
                    PEELOGGER.debug("DB TIMER: Closing idle connection")
                    self.close_idle()

                self._idle_timer = threading.Timer(60, close_idle_connection)
                self._idle_timer.daemon = True
                self._idle_timer.start()
                PEELOGGER.debug("DB TIMER: Started 60s idle timer")
        return conn



pacs_db = LoggingPooledMySQLDatabase(
    dbconfig.db,
    max_connections=5,
    stale_timeout=300,
    user=dbconfig.user,
    password=dbconfig.passwd,
    host=dbconfig.host,
    autoconnect=False)

migrator = MySQLMigrator(pacs_db)


def checkUpdates(db):
    cols = db.get_columns('study')
    found = False
    for col in cols:
        if col.name == 'nodeCode':
            found = True
            break
    if not found:
        migrate(
            migrator.add_column('study', 'nodeCode', CharField(default=''))
        )

    cols = db.get_columns('instance')
    found = False
    for col in cols:
        if col.name == 'frames':
            found = True
            break
    if not found:
        migrate(
            migrator.add_column('instance', 'frames', IntegerField(default=1))
        )

    "ALTER TABLE `instance` ADD COLUMN `size` INT(11) NOT NULL DEFAULT 0;"
    "ALTER TABLE `series` ADD COLUMN `size` INT(11) NOT NULL DEFAULT 0 AFTER `time`;"
    "ALTER TABLE `study` ADD COLUMN `size` INT(11) NOT NULL DEFAULT 0;"


class BaseModel(Model):
    class Meta: # pyright: ignore[reportIncompatibleVariableOverride]
        database = pacs_db
        auto_increment = False


class Patient(BaseModel):
    id = CharField(primary_key=True)
    name = CharField(index=True)
    birthday = DateField(index=True, null=True)
    sex = CharField(index=True, null=True)


class Study(BaseModel):
    uid = CharField(primary_key=True)
    id = CharField(index=True, null=True)
    date = DateField(index=True)
    time = CharField(null=True)
    description = CharField(null=True)
    accession_number = CharField(index=True, null=True)
    creator_aetitle = CharField(index=True, null=True)
    manufacturer_model = CharField(index=True, null=True)
    patient = ForeignKeyField(Patient, backref='studies')
    nodeCode = CharField()
    size = IntegerField(default=0)


class Series(BaseModel):
    uid = CharField(primary_key=True)
    modality = CharField(index=True)
    number = IntegerField()
    description = CharField(null=True)
    date = DateField(index=True, null=True)
    time = CharField(null=True)
    study = ForeignKeyField(Study, backref='series')
    size = IntegerField(default=0)


class Instance(BaseModel):
    uid = CharField(primary_key=True)
    study_date = DateField(index=True)
    number = IntegerField()
    frames = IntegerField(default=1)
    sopclass = CharField(index=True)
    md5 = CharField()
    bits_stored = CharField(null=True)
    high_bit = CharField(null=True)
    image_laterality = CharField(null=True)
    patient_orientation = CharField(null=True)
    photometric_interpretation = CharField(null=True)
    pixel_representation = CharField(null=True)
    pixel_spacing = CharField(null=True)
    rescale_intercept = CharField(null=True)
    rescale_slope = CharField(null=True)
    rows = IntegerField(null=True)
    columns = IntegerField(null=True)
    view_position = CharField(null=True)
    window_center = CharField(null=True)
    window_width = CharField(null=True)
    transfer_syntax = CharField(null=True)
    slice_location = CharField(null=True)
    slice_thickness = CharField(null=True)
    image_orientation_patient = CharField(null=True)
    image_position_patient = CharField(null=True)

    series = ForeignKeyField(Series, backref='instances')
    # study = ForeignKeyField(Study, backref='instances')
    size = IntegerField(default=0)


class SendLog(BaseModel):
    sent = DateTimeField(null=True, index=True)
    instance_uid = CharField(index=True)
    destination = CharField()


class User(BaseModel):
    id = int
    login = CharField(unique=True, max_length=64)
    passwd = CharField(max_length=255)  # bcrypt hash
    last_name = CharField(max_length=128)
    first_name = CharField(max_length=128)
    patr_name = CharField(max_length=128)
    is_admin = BooleanField(default=False)
    deleted = BooleanField(default=False)
    created_at = DateTimeField(default=datetime.datetime.now)
    updated_at = DateTimeField(default=datetime.datetime.now)

    class Meta: # pyright: ignore[reportIncompatibleVariableOverride]
        table_name = 'user'

    @classmethod
    def hash_password(cls, password: str) -> str:
        """Хеширует пароль с помощью bcrypt"""
        salt = bcrypt.gensalt(rounds=12)
        hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
        return hashed.decode('utf-8')

    @classmethod
    def check_password(cls, password: str, hashed: str) -> bool:
        """Проверяет пароль с помощью bcrypt"""
        return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))

    def set_password(self, password: str):
        """Устанавливает хешированный пароль"""
        self.passwd = self.hash_password(password)


class UserToken(BaseModel):
    """JWT токены аутентификации (вечные)"""
    user = ForeignKeyField(User, backref='tokens')
    token = CharField(unique=True, max_length=512)
    created_at = DateTimeField(default=datetime.datetime.now)

    class Meta: # pyright: ignore[reportIncompatibleVariableOverride]
        table_name = 'user_token'


class Worklist(BaseModel):
    """Записи worklist для модальностей"""
    id = int
    pat_id = CharField(max_length=64, index=True)  # PatientID
    pat_name = CharField(max_length=255, index=True)  # PatientName
    pat_birth = DateField()  # PatientBirthDate
    pat_sex = CharField(max_length=1)  # PatientSex (M/F/O)
    acc_num = CharField(max_length=64, null=True)  # AccessionNumber
    modality = CharField(max_length=16, null=True, index=True)  # Modality
    station_aet = CharField(max_length=16, null=True, index=True)  # ScheduledStationAETitle
    sched_date = DateField(index=True)  # ScheduledProcedureStepStartDate (NOT NULL)
    sched_time = TimeField(null=True)  # ScheduledProcedureStepStartTime
    req_desc = CharField(max_length=255, null=True)  # RequestedProcedureDescription
    study_uid = CharField(max_length=64, null=True, unique=True)  # StudyInstanceUID после выполнения
    created_at = DateTimeField(default=datetime.datetime.now)
    updated_at = DateTimeField(default=datetime.datetime.now)

    class Meta: # pyright: ignore[reportIncompatibleVariableOverride]
        table_name = 'worklist'


class StudyDescription(BaseModel):
    """Описания и заключения исследований"""
    study = ForeignKeyField(Study, field='uid', backref='descriptions', unique=True)
    desc_text = TextField()  # описание
    conclusion = TextField()  # заключение
    created_by = ForeignKeyField(User, backref='created_descriptions')
    created_at = DateTimeField(default=datetime.datetime.now)
    updated_at = DateTimeField(default=datetime.datetime.now)
    updated_by = ForeignKeyField(User, null=True, backref='updated_descriptions')

    class Meta: # pyright: ignore[reportIncompatibleVariableOverride]
        table_name = 'study_description'
