"""Digital Employee Builder.

This module provides the DigitalEmployeeBuilder class for creating
DigitalEmployee instances with a fluent interface.

Authors:
    Immanuel Rhesa (immanuel.rhesa@gdplabs.id)

References:
    NONE
"""

from dataclasses import dataclass, field
from typing import Self

from gllm_pipeline.pipeline.pipeline import Pipeline

from digital_employee_core.digital_employee.builders.pipeline_builder import (
    DigitalEmployeePipelineBuilder,
)
from digital_employee_core.digital_employee.config import DigitalEmployeeConfig
from digital_employee_core.digital_employee.core import DigitalEmployee
from digital_employee_core.identity.identity import (
    DigitalEmployeeIdentity,
    DigitalEmployeeJob,
    DigitalEmployeeSupervisor,
)


@dataclass
class DigitalEmployeeBuilder:
    """Builder for creating DigitalEmployee instances with a fluent interface.

    This builder provides a convenient way to construct a DigitalEmployee
    by chaining method calls. It handles the creation of Identity, Configuration
    objects internally, and can set identity for agents in the pipeline.

    Attributes:
        _name (str | None): The name of the digital employee.
        _email (str | None): The email of the digital employee.
        _job (DigitalEmployeeJob | None): The job information.
        _supervisor_name (str | None): The name of the supervisor.
        _supervisor_email (str | None): The email of the supervisor.
        _configurations (list[DigitalEmployeeConfig]): List of configuration objects.
        _pipeline_builder (DigitalEmployeePipelineBuilder | None): The pipeline builder.
        _pipeline (Pipeline | None): The pipeline instance.

    Example:
        digital_employee = (
            DigitalEmployeeBuilder()
            .set_name("Claudia")
            .set_email("claudia@example.com")
            .set_job("Digital Assistant", "A helpful assistant", "Be helpful and professional")
            .set_pipeline(DigitalEmployeePipelineBuilder()
                .add_step(GuardrailsBuilder())
                .add_step(DigitalEmployeeAgentBuilder().mcps(["google"]))
            )
            .add_configs_from_env(["API_KEY", "SERVICE_URL"])
            .build()
        )
    """

    _name: str | None = field(default=None, init=False)
    _email: str | None = field(default=None, init=False)
    _job: DigitalEmployeeJob | None = field(default=None, init=False)
    _supervisor_name: str | None = field(default=None, init=False)
    _supervisor_email: str | None = field(default=None, init=False)
    _configurations: list[DigitalEmployeeConfig] = field(default_factory=list, init=False)
    _pipeline_builder: DigitalEmployeePipelineBuilder | None = field(default=None, init=False)
    _pipeline: Pipeline | None = field(default=None, init=False)

    def set_name(self, name: str) -> Self:
        """Set the digital employee's name.

        Args:
            name (str): The name of the digital employee.

        Returns:
            Self: The builder instance for chaining.
        """
        self._name = name
        return self

    def set_email(self, email: str) -> Self:
        """Set the digital employee's email.

        Args:
            email (str): The digital employee's email address.

        Returns:
            Self: The builder instance for chaining.
        """
        self._email = email
        return self

    def set_job(self, title: str, description: str, instruction: str) -> Self:
        """Set the digital employee's job information.

        Args:
            title (str): The job title or role name.
            description (str): Detailed description of the job and responsibilities.
            instruction (str): Specific instructions or guidelines for performing the job.

        Returns:
            Self: The builder instance for chaining.
        """
        self._job = DigitalEmployeeJob(title=title, description=description, instruction=instruction)
        return self

    def set_supervisor(self, name: str, email: str) -> Self:
        """Set the digital employee's supervisor information.

        Args:
            name (str): The supervisor's name.
            email (str): The supervisor's email address.

        Returns:
            Self: The builder instance for chaining.
        """
        self._supervisor_name = name
        self._supervisor_email = email
        return self

    def add_configs(self, configurations: list[DigitalEmployeeConfig]) -> Self:
        """Add configurations to the digital employee.

        Args:
            configurations (list[DigitalEmployeeConfig]): Configuration objects.

        Returns:
            Self: The builder instance for chaining.
        """
        self._configurations.extend(configurations)
        return self

    def add_configs_from_env(self, keys: list[str] | list[tuple[str, str]]) -> Self:
        """Add configurations from environment variables.

        Args:
            keys (list[str] | list[tuple[str, str]]): List of environment variable keys to load as configurations.
                Each item can be either:
                - A string key (uses empty string as default if not found)
                - A tuple of (key, default_value)

        Returns:
            Self: The builder instance for chaining.

        Examples:
            >>> builder.add_configs_from_env(["KEY1", "KEY2"])
            >>> builder.add_configs_from_env([("KEY1", "default1"), ("KEY2", "default2")])
            >>> builder.add_configs_from_env(["KEY1", ("KEY2", "default2")])
        """
        self._configurations.extend(DigitalEmployeeConfig.from_env(*keys))
        return self

    def set_pipeline(self, pipeline: Pipeline | DigitalEmployeePipelineBuilder) -> Self:
        """Set the pipeline for the digital employee.

        Args:
            pipeline (Pipeline | DigitalEmployeePipelineBuilder): The pipeline or pipeline builder.

        Returns:
            Self: The builder instance for chaining.
        """
        if isinstance(pipeline, DigitalEmployeePipelineBuilder):
            self._pipeline_builder = pipeline
            self._pipeline = None
        else:
            self._pipeline = pipeline
            self._pipeline_builder = None
        return self

    def build(self) -> DigitalEmployee:
        """Build the DigitalEmployee instance.

        Returns:
            DigitalEmployee: The created Digital Employee instance.

        Raises:
            ValueError: If required fields (name, email, job information) are not set.
        """
        if not self._name:
            raise ValueError("Name is required. Use set_name() to set it.")
        if not self._email:
            raise ValueError("Email is required. Use set_email() to set it.")
        if not self._job:
            raise ValueError("Job information is required. Use set_job() to set title, description, and instruction.")

        supervisor = None
        if self._supervisor_name and self._supervisor_email:
            supervisor = DigitalEmployeeSupervisor(name=self._supervisor_name, email=self._supervisor_email)

        identity = DigitalEmployeeIdentity(name=self._name, email=self._email, job=self._job, supervisor=supervisor)

        pipeline = self._pipeline
        if self._pipeline_builder is not None:
            pipeline = self._pipeline_builder.build(identity, self._configurations)

        if pipeline is None:
            raise ValueError("Pipeline is required. Use set_pipeline() to set it.")

        return DigitalEmployee(identity=identity, pipeline=pipeline, configs=self._configurations)
