from pydantic import BaseModel, ConfigDict
from typing import List, Union, Optional
import time
import logging

from sqlalchemy import Column, String, Text, BigInteger, Boolean

from apps.webui.internal.db import JSONField, Base, Session
from apps.webui.models.users import Users

import json
import copy


from config import SRC_LOG_LEVELS

log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"])

####################
# Functions DB Schema
####################


class Function(Base):
    __tablename__ = "function"

    id = Column(String, primary_key=True)
    user_id = Column(String)
    name = Column(Text)
    type = Column(Text)
    content = Column(Text)
    meta = Column(JSONField)
    valves = Column(JSONField)
    is_active = Column(Boolean)
    is_global = Column(Boolean)
    updated_at = Column(BigInteger)
    created_at = Column(BigInteger)


class FunctionMeta(BaseModel):
    description: Optional[str] = None
    manifest: Optional[dict] = {}


class FunctionModel(BaseModel):
    id: str
    user_id: str
    name: str
    type: str
    content: str
    meta: FunctionMeta
    is_active: bool = False
    is_global: bool = False
    updated_at: int  # timestamp in epoch
    created_at: int  # timestamp in epoch

    model_config = ConfigDict(from_attributes=True)


####################
# Forms
####################


class FunctionResponse(BaseModel):
    id: str
    user_id: str
    type: str
    name: str
    meta: FunctionMeta
    is_active: bool
    is_global: bool
    updated_at: int  # timestamp in epoch
    created_at: int  # timestamp in epoch


class FunctionForm(BaseModel):
    id: str
    name: str
    content: str
    meta: FunctionMeta


class FunctionValves(BaseModel):
    valves: Optional[dict] = None


class FunctionsTable:

    def insert_new_function(
        self, user_id: str, type: str, form_data: FunctionForm
    ) -> Optional[FunctionModel]:
        function = FunctionModel(
            **{
                **form_data.model_dump(),
                "user_id": user_id,
                "type": type,
                "updated_at": int(time.time()),
                "created_at": int(time.time()),
            }
        )

        try:
            result = Function(**function.model_dump())
            Session.add(result)
            Session.commit()
            Session.refresh(result)
            if result:
                return FunctionModel.model_validate(result)
            else:
                return None
        except Exception as e:
            print(f"Error creating tool: {e}")
            return None

    def get_function_by_id(self, id: str) -> Optional[FunctionModel]:
        try:
            function = Session.get(Function, id)
            return FunctionModel.model_validate(function)
        except:
            return None

    def get_functions(self, active_only=False) -> List[FunctionModel]:
        if active_only:
            return [
                FunctionModel.model_validate(function)
                for function in Session.query(Function).filter_by(is_active=True).all()
            ]
        else:
            return [
                FunctionModel.model_validate(function)
                for function in Session.query(Function).all()
            ]

    def get_functions_by_type(
        self, type: str, active_only=False
    ) -> List[FunctionModel]:
        if active_only:
            return [
                FunctionModel.model_validate(function)
                for function in Session.query(Function)
                .filter_by(type=type, is_active=True)
                .all()
            ]
        else:
            return [
                FunctionModel.model_validate(function)
                for function in Session.query(Function).filter_by(type=type).all()
            ]

    def get_global_filter_functions(self) -> List[FunctionModel]:
        return [
            FunctionModel(**model_to_dict(function))
            for function in Function.select().where(
                Function.type == "filter",
                Function.is_active == True,
                Function.is_global == True,
            )
        ]

    def get_function_valves_by_id(self, id: str) -> Optional[dict]:
        try:
            function = Session.get(Function, id)
            return function.valves if function.valves else {}
        except Exception as e:
            print(f"An error occurred: {e}")
            return None

    def update_function_valves_by_id(
        self, id: str, valves: dict
    ) -> Optional[FunctionValves]:
        try:
            function = Session.get(Function, id)
            function.valves = valves
            function.updated_at = int(time.time())
            Session.commit()
            Session.refresh(function)
            return self.get_function_by_id(id)
        except:
            return None

    def get_user_valves_by_id_and_user_id(
        self, id: str, user_id: str
    ) -> Optional[dict]:
        try:
            user = Users.get_user_by_id(user_id)
            user_settings = user.settings.model_dump()

            # Check if user has "functions" and "valves" settings
            if "functions" not in user_settings:
                user_settings["functions"] = {}
            if "valves" not in user_settings["functions"]:
                user_settings["functions"]["valves"] = {}

            return user_settings["functions"]["valves"].get(id, {})
        except Exception as e:
            print(f"An error occurred: {e}")
            return None

    def update_user_valves_by_id_and_user_id(
        self, id: str, user_id: str, valves: dict
    ) -> Optional[dict]:
        try:
            user = Users.get_user_by_id(user_id)
            user_settings = user.settings.model_dump()

            # Check if user has "functions" and "valves" settings
            if "functions" not in user_settings:
                user_settings["functions"] = {}
            if "valves" not in user_settings["functions"]:
                user_settings["functions"]["valves"] = {}

            user_settings["functions"]["valves"][id] = valves

            # Update the user settings in the database
            Users.update_user_by_id(user_id, {"settings": user_settings})

            return user_settings["functions"]["valves"][id]
        except Exception as e:
            print(f"An error occurred: {e}")
            return None

    def update_function_by_id(self, id: str, updated: dict) -> Optional[FunctionModel]:
        try:
            Session.query(Function).filter_by(id=id).update(
                {
                    **updated,
                    "updated_at": int(time.time()),
                }
            )
            Session.commit()
            return self.get_function_by_id(id)
        except:
            return None

    def deactivate_all_functions(self) -> Optional[bool]:
        try:
            Session.query(Function).update(
                {
                    "is_active": False,
                    "updated_at": int(time.time()),
                }
            )
            Session.commit()
            return True
        except:
            return None

    def delete_function_by_id(self, id: str) -> bool:
        try:
            Session.query(Function).filter_by(id=id).delete()
            return True
        except:
            return False


Functions = FunctionsTable()
