from typing import Any

from sqlmodel import SQLModel

from common.database.utils import find_primary_key


class NotFoundInDatabaseError(Exception):
    pass


def get_job_parameters(
    job_fn: callable, ensure_sql_model: bool = True, *, owner: Any = None
) -> dict:
    retval = {}
    retval["method_name"] = job_fn.__name__

    # Check if the function is bound
    if hasattr(job_fn, "__self__") and owner is None:
        owner = job_fn.__self__

    if owner is not None:
        # Bound instance method
        if isinstance(owner, SQLModel):
            if ensure_sql_model:
                assert isinstance(owner, SQLModel), "The class must be a SQLModel"

            # Get the class name and module
            retval["model_name"] = owner.__class__.__name__
            retval["module_name"] = (
                owner.__class__.__module__
            )  # Add module name of the instance's class

            # Get the primary key name
            primary_key_name = find_primary_key(owner.__class__)
            if primary_key_name is not None:
                retval["instance_primary_key"] = getattr(owner, primary_key_name)
        # Classmethod: bound to the class object
        elif isinstance(owner, type) and (
            not ensure_sql_model or issubclass(owner, SQLModel)
        ):
            retval["model_name"] = owner.__name__
            retval["module_name"] = owner.__module__
            # Note: No instance_primary_key for class methods
        else:
            if ensure_sql_model:
                raise AssertionError(
                    "The owner must be a SQLModel instance or SQLModel subclass for class methods."
                )
    elif not ensure_sql_model:
        # Unbound method - try to infer class from __qualname__
        # e.g., "MaintenanceTasks.example_task" -> model_name="MaintenanceTasks"
        qualname = getattr(job_fn, "__qualname__", None)
        module = getattr(job_fn, "__module__", None)

        if qualname and module and "." in qualname:
            # Reject functions defined inside other functions (closures)
            # e.g., "outer.<locals>.Inner.method" - can't be resolved by executor
            if "<locals>" in qualname:
                raise ValueError(
                    f"Cannot create job from locally-defined class method: {qualname}. "
                    f"The class must be defined at module level."
                )

            # Split qualname: "OuterClass.InnerClass.method" -> class is second-to-last
            parts = qualname.rsplit(".", 1)
            if len(parts) == 2:
                retval["model_name"] = parts[0].split(".")[-1]  # Take innermost class
                retval["module_name"] = module
                # Note: No instance_primary_key for unbound methods

    return retval
