import logging
from datetime import datetime
from typing import Optional

from afnio.tellurio._client_manager import get_default_clients
from afnio.tellurio.client import TellurioClient

logger = logging.getLogger(__name__)


class Project:
    """
    Represents a [Tellurio Studio](https://platform.tellurio.ai/) Project
    with detailed information.

    A [`Project`][.] represents a workspace for organizing and managing related
    [`Run`][afnio.tellurio.run.Run]s. Each [`Project`][.] belongs to a namespace,
    which can be either an Organization or a User (personal Organization).
    The project contains metadata such as its unique identifier, display name,
    slug, creation date, last run date, visibility level, and the user's role
    within the project.
    """

    def __init__(
        self,
        uuid: str,
        display_name: str,
        slug: str,
        date_created: datetime,
        last_run_date: Optional[datetime],
        visibility: str,
        role: Optional[str],
        org_uuid: Optional[str],
        org_display_name: Optional[str],
        org_slug: Optional[str],
    ):
        """Initializes a `Project` instance.

        Args:
            uuid: The unique identifier of the project.
            display_name: The display name of the project.
            slug: The slug of the project.
            date_created: The date and time when the project was created.
            last_run_date: The date and time of the last [`Run`][afnio.tellurio.run.Run]
                in the project, or `None` if no runs exist.
            visibility: The visibility level of the project (e.g., `"RESTRICTED"`,
                `"TEAM"`, `"PUBLIC"`, `"OPEN"`).
            role: The user's role in the project (e.g., `"OWNER"`, `"PARTICIPANT"`),
                or `None` if not applicable.
            org_uuid: The unique identifier of the organization that owns the project.
            org_display_name: The display name of the organization that owns
                the project.
            org_slug: The slug of the organization that owns the project.
        """
        self.uuid = uuid
        self.display_name = display_name
        self.slug = slug
        self.date_created = date_created
        self.last_run_date = last_run_date
        self.visibility = visibility
        self.role = role
        self.org_uuid = org_uuid
        self.org_display_name = org_display_name
        self.org_slug = org_slug

    def __repr__(self):
        return (
            f"<Project uuid={self.uuid} display_name={self.display_name} "
            f"visibility={self.visibility} role={self.role}>"
        )


def get_project(
    namespace_slug: str,
    project_slug: str,
    client: Optional[TellurioClient] = None,
) -> Optional[Project]:
    """Retrieves a project by its slug in the specified namespace.

    Args:
        namespace_slug: The namespace slug where the project resides. It can be
          either an organization slug or a user slug.
        project_slug: The slug of the project to retrieve.
        client: An instance of [`TellurioClient`][afnio.tellurio.client.TellurioClient].
            If not provided, the default client will be used.

    Returns:
        A Project object representing the retrieved project or `None` if not found.

    Raises:
        Exception: If an unexpected error occurs during the request.
    """
    client = client or get_default_clients()[0]

    # Define the endpoint
    endpoint = f"/api/v0/{namespace_slug}/projects/{project_slug}/"

    try:
        response = client.get(endpoint)

        if response.status_code == 200:
            data = response.json()
            logger.debug(f"Project retrieved successfully: {data}")

            # Parse date fields
            date_created = datetime.fromisoformat(
                data["date_created"].replace("Z", "+00:00")
            )
            last_run_date = (
                datetime.fromisoformat(data["last_run_date"].replace("Z", "+00:00"))
                if data.get("last_run_date")
                else None
            )

            # Create and return the Project object
            return Project(
                uuid=data["uuid"],
                display_name=data["display_name"],
                slug=data["slug"],
                date_created=date_created,
                last_run_date=last_run_date,
                visibility=data["visibility"],
                role=data.get("role"),
                org_uuid=data.get("org_uuid"),
                org_display_name=data.get("org_display_name"),
                org_slug=data.get("org_slug"),
            )
        elif response.status_code == 404:
            logger.debug(
                f"Project with slug '{project_slug}' "
                f"not found in namespace '{namespace_slug}'."
            )
            return None
        else:
            logger.error(
                f"Failed to retrieve project: {response.status_code} - {response.text}"
            )
            response.raise_for_status()
    except Exception as e:
        logger.error(f"An error occurred while retrieving the project: {e}")
        raise


def create_project(
    namespace_slug: str,
    display_name: str,
    visibility: str = "TEAM",
    client: Optional[TellurioClient] = None,
) -> Project:
    """Creates a new project in the specified namespace.

    Args:
        namespace_slug: The namespace slug where the project resides. It can be
          either an organization slug or a user slug.
        display_name: The display name of the project.
        visibility: The visibility of the project.
        client: An instance of [`TellurioClient`][afnio.tellurio.client.TellurioClient].
            If not provided, the default client will be used.

    Returns:
        A Project object representing the created project.

    Raises:
        Exception: If an unexpected error occurs during the request.
    """
    client = client or get_default_clients()[0]

    # Define the endpoint and payload
    endpoint = f"/api/v0/{namespace_slug}/projects/"
    payload = {
        "display_name": display_name,
        "visibility": visibility,
    }

    try:
        response = client.post(endpoint, json=payload)

        if response.status_code == 201:
            data = response.json()
            logger.debug(f"Project created successfully: {data}")

            # Parse date fields
            date_created = datetime.fromisoformat(
                data["date_created"].replace("Z", "+00:00")
            )
            last_run_date = (
                datetime.fromisoformat(data["last_run_date"].replace("Z", "+00:00"))
                if data.get("last_run_date")
                else None
            )

            # Create and return the Project object
            return Project(
                uuid=data["uuid"],
                display_name=data["display_name"],
                slug=data["slug"],
                date_created=date_created,
                last_run_date=last_run_date,
                visibility=data["visibility"],
                role=data["role"],
                org_uuid=data["org_uuid"],
                org_display_name=data["org_display_name"],
                org_slug=data["org_slug"],
            )
        else:
            logger.error(
                f"Failed to create project: {response.status_code} - {response.text}"
            )
            response.raise_for_status()
    except Exception as e:
        logger.error(f"An error occurred while creating the project: {e}")
        raise


def delete_project(
    namespace_slug: str,
    project_slug: str,
    client: Optional[TellurioClient] = None,
) -> None:
    """Deletes a project by the specified namespace.

    Args:
        namespace_slug: The namespace slug where the project resides. It can be
          either an organization slug or a user slug.
        project_slug: The slug of the project to delete.
        client: An instance of [`TellurioClient`][afnio.tellurio.client.TellurioClient].
            If not provided, the default client will be used.

    Raises:
        Exception: If an unexpected error occurs during the request.
    """
    client = client or get_default_clients()[0]

    # Define the endpoint
    endpoint = f"/api/v0/{namespace_slug}/projects/{project_slug}/"

    try:
        response = client.delete(endpoint)

        if response.status_code == 204:
            logger.debug(f"Project '{project_slug}' deleted successfully.")
        else:
            logger.error(
                f"Failed to delete project: {response.status_code} - {response.text}"
            )
            response.raise_for_status()
    except Exception as e:
        logger.error(f"An error occurred while deleting the project: {e}")
        raise
