from sqlalchemy.dialects.postgresql import JSONB

from ..extensions import db
from .user import User, Tenant, TenantMake
from .document import Embedding, Retriever


class ChatSession(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    session_id = db.Column(db.String(49), nullable=True)
    session_start = db.Column(db.DateTime, nullable=False)
    session_end = db.Column(db.DateTime, nullable=True)
    timezone = db.Column(db.String(30), nullable=True)

    # Relations
    interactions = db.relationship('Interaction', backref='chat_session', lazy=True)

    def __repr__(self):
        return f"<ChatSession {self.id} by {self.user_id}>"


class Specialist(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    tuning = db.Column(db.Boolean, nullable=True, default=False)
    configuration = db.Column(JSONB, nullable=True)
    arguments = db.Column(JSONB, nullable=True)
    active = db.Column(db.Boolean, nullable=True, default=True)

    # Relationship to retrievers through the association table
    retrievers = db.relationship('SpecialistRetriever', backref='specialist', lazy=True,
                                 cascade="all, delete-orphan")
    agents = db.relationship('EveAIAgent', backref='specialist', lazy=True)
    tasks = db.relationship('EveAITask', backref='specialist', lazy=True)
    tools = db.relationship('EveAITool', backref='specialist', lazy=True)
    dispatchers = db.relationship('SpecialistDispatcher', backref='specialist', lazy=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))

    def __repr__(self):
        return f"<Specialist {self.id}: {self.name}>"

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'type': self.type,
            'type_version': self.type_version,
            'configuration': self.configuration,
            'arguments': self.arguments,
            'active': self.active,
        }


class EveAIAsset(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="DOCUMENT_TEMPLATE")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")

    # Storage information
    bucket_name = db.Column(db.String(255), nullable=True)
    object_name = db.Column(db.String(200), nullable=True)
    file_type = db.Column(db.String(20), nullable=True)
    file_size = db.Column(db.Float, nullable=True)

    # Metadata information
    user_metadata = db.Column(JSONB, nullable=True)
    system_metadata = db.Column(JSONB, nullable=True)

    # Configuration information
    configuration = db.Column(JSONB, nullable=True)

    # Cost information
    prompt_tokens = db.Column(db.Integer, nullable=True)
    completion_tokens = db.Column(db.Integer, nullable=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))

    last_used_at = db.Column(db.DateTime, nullable=True)


class EveAIDataCapsule(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    chat_session_id = db.Column(db.Integer, db.ForeignKey(ChatSession.id), nullable=False)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    configuration = db.Column(JSONB, nullable=True)
    data = db.Column(JSONB, nullable=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))

    # Unieke constraint voor chat_session_id, type en type_version
    __table_args__ = (db.UniqueConstraint('chat_session_id', 'type', 'type_version', name='uix_data_capsule_session_type_version'),)


class EveAIAgent(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id), nullable=False)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    role = db.Column(db.Text, nullable=True)
    goal = db.Column(db.Text, nullable=True)
    backstory = db.Column(db.Text, nullable=True)
    tuning = db.Column(db.Boolean, nullable=True, default=False)
    configuration = db.Column(JSONB, nullable=True)
    arguments = db.Column(JSONB, nullable=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))


class EveAITask(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id), nullable=False)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    task_description = db.Column(db.Text, nullable=True)
    expected_output = db.Column(db.Text, nullable=True)
    tuning = db.Column(db.Boolean, nullable=True, default=False)
    configuration = db.Column(JSONB, nullable=True)
    arguments = db.Column(JSONB, nullable=True)
    context = db.Column(JSONB, nullable=True)
    asynchronous = db.Column(db.Boolean, nullable=True, default=False)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))


class EveAITool(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id), nullable=False)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    tuning = db.Column(db.Boolean, nullable=True, default=False)
    configuration = db.Column(JSONB, nullable=True)
    arguments = db.Column(JSONB, nullable=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))


class Dispatcher(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    type = db.Column(db.String(50), nullable=False, default="STANDARD_RAG")
    type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
    tuning = db.Column(db.Boolean, nullable=True, default=False)
    configuration = db.Column(JSONB, nullable=True)
    arguments = db.Column(JSONB, nullable=True)

    # Versioning Information
    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))


class Interaction(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    chat_session_id = db.Column(db.Integer, db.ForeignKey(ChatSession.id), nullable=False)
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id), nullable=True)
    specialist_arguments = db.Column(JSONB, nullable=True)
    specialist_results = db.Column(JSONB, nullable=True)
    timezone = db.Column(db.String(30), nullable=True)
    appreciation = db.Column(db.Integer, nullable=True)

    # Timing information
    question_at = db.Column(db.DateTime, nullable=False)
    detailed_question_at = db.Column(db.DateTime, nullable=True)
    answer_at = db.Column(db.DateTime, nullable=True)
    processing_error = db.Column(db.String(255), nullable=True)

    # Relations
    embeddings = db.relationship('InteractionEmbedding', backref='interaction', lazy=True)

    def __repr__(self):
        return f"<Interaction {self.id}>"


class InteractionEmbedding(db.Model):
    interaction_id = db.Column(db.Integer, db.ForeignKey(Interaction.id, ondelete='CASCADE'), primary_key=True)
    embedding_id = db.Column(db.Integer, db.ForeignKey(Embedding.id, ondelete='CASCADE'), primary_key=True)


class SpecialistRetriever(db.Model):
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id, ondelete='CASCADE'), primary_key=True)
    retriever_id = db.Column(db.Integer, db.ForeignKey(Retriever.id, ondelete='CASCADE'), primary_key=True)

    retriever = db.relationship("Retriever", backref="specialist_retrievers")


class SpecialistDispatcher(db.Model):
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id, ondelete='CASCADE'), primary_key=True)
    dispatcher_id = db.Column(db.Integer, db.ForeignKey(Dispatcher.id, ondelete='CASCADE'), primary_key=True)

    dispatcher = db.relationship("Dispatcher", backref="specialist_dispatchers")


class SpecialistMagicLink(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    description = db.Column(db.Text, nullable=True)
    specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id, ondelete='CASCADE'), nullable=False)
    tenant_make_id = db.Column(db.Integer, db.ForeignKey(TenantMake.id, ondelete='CASCADE'), nullable=True)
    magic_link_code = db.Column(db.String(55), nullable=False, unique=True)

    valid_from = db.Column(db.DateTime, nullable=True)
    valid_to = db.Column(db.DateTime, nullable=True)

    specialist_args = db.Column(JSONB, nullable=True)

    created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
    updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
    updated_by = db.Column(db.Integer, db.ForeignKey(User.id))

    def __repr__(self):
        return f"<SpecialistMagicLink {self.specialist_id} {self.magic_link_code}>"

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'magic_link_code': self.magic_link_code,
            'valid_from': self.valid_from,
            'valid_to': self.valid_to,
            'specialist_args': self.specialist_args,
        }
