# *************************************************************************
#
#  Copyright (c) 2026 - Datatailr Inc.
#  All Rights Reserved.
#
#  This file is part of Datatailr and subject to the terms and conditions
#  defined in 'LICENSE.txt'. Unauthorized copying and/or distribution
#  of this file, in parts or full, via any medium is strictly prohibited.
# *************************************************************************

from typing import Dict, List, Optional, Union, Callable
from datatailr.build import Image
from datatailr import ACL, Environment, User
from datatailr.scheduler.base import Job, JobType, EntryPoint, Resources


class App(Job):
    """
    Represents a web application or a dashboard deployment on Datatailr.
    This could be a Streamlit app, Dash app, or any other web-based data application.
    The implementation of the app does not need to follow any specific framework, as long as it can be started
    in the standard way (e.g., `streamlit run app.py`).

    Example:
    ```python
    # app.py
    import streamlit as st

    def main():
        st.title("Hello Datatailr App")

    if __name__ == "__main__":
        main()
    ```
    The app can be tested locally by running:
    ```bash
    streamlit run app.py
    ```

    To deploy the app to Datatailr, you would create an `App` job in your Python code:
    ```python
    from app import main
    from datatailr import App

    app = App(
        name="Simple Dashboard App",
        entrypoint=main,
        python_requirements="streamlit")

    app.run()
    ```
    This will package and deploy the app to the Datatailr platform. The app definition and deployment status can be viewed at:

    [https://YOUR-DOMAIN.datatailr.com/job-details/simple-dashboard-app/1/dev/definition](https://YOUR-DOMAIN.datatailr.com/job-details/simple-dashboard-app/1/dev/definition)

    The deployed app can be accessed by all users with the appropriate permissions from the app launcher page or directly via its URL:

    [https://YOUR-DOMAIN.datatailr.com/job/dev/simple-dashboard-app/](https://YOUR-DOMAIN.datatailr.com/job/dev/simple-dashboard-app/)
    """

    def __init__(
        self,
        name: str,
        entrypoint: Callable,
        environment: Optional[Environment] = Environment.DEV,
        image: Optional[Image] = None,
        run_as: Optional[Union[str, User]] = None,
        resources: Resources = Resources(),
        acl: Optional[ACL] = None,
        framework: Optional[str] = None,
        python_version: str = "3.12",
        python_requirements: str | List[str] = "",
        build_script_pre: str = "",
        build_script_post: str = "",
        env_vars: Dict[str, str | int | float | bool] = {},
        update_existing: bool = False,
    ):
        """Initialize an App deployment.

        Args:
            name: Display name for the app.
            entrypoint: The callable (function) that starts the application.
            environment: Target environment for the deployment.
            image: Pre-configured container Image. When ``None``, an image is
                built from *python_version*, *python_requirements*, and the
                build scripts.
            run_as: User or username to run the app as.  Defaults to the
                currently signed-in user.
            resources: CPU and memory resources for the container.
            acl: Access control list.  Defaults to standard permissions for the
                current user.
            framework: Optional framework name (e.g., 'streamlit', 'dash',
                'flask').  If not provided, the framework is inferred from the
                entrypoint.
            python_version: Python version for the container image.
            python_requirements: Python dependencies (see ``Image``).
            build_script_pre: Dockerfile commands to run before installing
                requirements.
            build_script_post: Dockerfile commands to run after installing
                requirements.
            env_vars: Environment variables passed to the running container.
            update_existing: If ``True``, load and update an existing job
                definition with the same name instead of creating a new one.
        """
        entrypoint = EntryPoint(JobType.APP, entrypoint)
        if framework and "DATATAILR_FRAMEWORK" not in env_vars:
            env_vars["DATATAILR_FRAMEWORK"] = framework

        super().__init__(
            name=name,
            type=JobType.APP,
            entrypoint=entrypoint,
            environment=environment,
            image=image,
            run_as=run_as,
            resources=resources,
            acl=acl,
            python_version=python_version,
            python_requirements=python_requirements,
            build_script_pre=build_script_pre,
            build_script_post=build_script_post,
            env_vars=env_vars,
            update_existing=update_existing,
        )


class Service(Job):
    """Represents a long-running background service deployment on Datatailr.

    A service runs continuously (e.g., an API server, a message consumer, or
    any always-on process).  It is restarted automatically if it exits.

    Example:
        ```python
        from datatailr import Service

        def run_server():
            import uvicorn
            uvicorn.run("myapi:app", host="0.0.0.0", port=8000)

        svc = Service(
            name="My API Service",
            entrypoint=run_server,
            python_requirements="fastapi,uvicorn",
        )
        svc.run()
        ```
    """

    def __init__(
        self,
        name: str,
        entrypoint: Callable,
        environment: Optional[Environment] = Environment.DEV,
        image: Optional[Image] = None,
        run_as: Optional[Union[str, User]] = None,
        resources: Resources = Resources(),
        acl: Optional[ACL] = None,
        python_version: str = "3.12",
        python_requirements: str | List[str] = "",
        build_script_pre: str = "",
        build_script_post: str = "",
        env_vars: Dict[str, str | int | float | bool] = {},
        update_existing: bool = False,
    ):
        """Initialize a Service deployment.

        Args:
            name: Display name for the service.
            entrypoint: The callable (function) that starts the service.
            environment: Target environment for the deployment.
            image: Pre-configured container Image.
            run_as: User or username to run the service as.
            resources: CPU and memory resources for the container.
            acl: Access control list.
            python_version: Python version for the container image.
            python_requirements: Python dependencies (see ``Image``).
            build_script_pre: Dockerfile commands to run before installing
                requirements.
            build_script_post: Dockerfile commands to run after installing
                requirements.
            env_vars: Environment variables passed to the running container.
            update_existing: If ``True``, update an existing job definition.
        """
        entrypoint = EntryPoint(JobType.SERVICE, entrypoint)
        super().__init__(
            name=name,
            type=JobType.SERVICE,
            entrypoint=entrypoint,
            environment=environment,
            image=image,
            run_as=run_as,
            resources=resources,
            acl=acl,
            python_version=python_version,
            python_requirements=python_requirements,
            build_script_pre=build_script_pre,
            build_script_post=build_script_post,
            env_vars=env_vars,
            update_existing=update_existing,
        )


class ExcelAddin(Job):
    """Represents an Excel add-in deployment on Datatailr.

    An Excel add-in exposes Python functions as Excel worksheet functions,
    allowing users to call server-side computations directly from Excel
    spreadsheets.

    Example:
        ```python
        from datatailr import ExcelAddin

        def price_option(spot, strike, vol, rate, expiry):
            # Black-Scholes pricing logic
            ...

        addin = ExcelAddin(
            name="Options Pricer",
            entrypoint=price_option,
            python_requirements="numpy,scipy",
        )
        addin.run()
        ```
    """

    def __init__(
        self,
        name: str,
        entrypoint: Callable,
        environment: Optional[Environment] = Environment.DEV,
        image: Optional[Image] = None,
        run_as: Optional[Union[str, User]] = None,
        resources: Resources = Resources(),
        acl: Optional[ACL] = None,
        python_version: str = "3.12",
        python_requirements: str | List[str] = "",
        build_script_pre: str = "",
        build_script_post: str = "",
        env_vars: Dict[str, str | int | float | bool] = {},
        update_existing: bool = False,
    ):
        """Initialize an Excel add-in deployment.

        Args:
            name: Display name for the add-in.
            entrypoint: The callable (function) to expose as an Excel function.
            environment: Target environment for the deployment.
            image: Pre-configured container Image.
            run_as: User or username to run the add-in as.
            resources: CPU and memory resources for the container.
            acl: Access control list.
            python_version: Python version for the container image.
            python_requirements: Python dependencies (see ``Image``).
            build_script_pre: Dockerfile commands to run before installing
                requirements.
            build_script_post: Dockerfile commands to run after installing
                requirements.
            env_vars: Environment variables passed to the running container.
            update_existing: If ``True``, update an existing job definition.
        """
        entrypoint = EntryPoint(JobType.EXCEL, entrypoint)
        super().__init__(
            name=name,
            type=JobType.EXCEL,
            entrypoint=entrypoint,
            environment=environment,
            image=image,
            run_as=run_as,
            resources=resources,
            acl=acl,
            python_version=python_version,
            python_requirements=python_requirements,
            build_script_pre=build_script_pre,
            build_script_post=build_script_post,
            env_vars=env_vars,
            update_existing=update_existing,
        )
