"""Credentials create command implementation."""

import json
from datetime import datetime
from typing import Any, Optional

import typer

from cli.commands.credentials.create.presenter import (
    present_org_credential_create_success,
)
from cli.commands.flag_utils import validate_provider
from cli.commands.shared_flags import (
    CREDENTIAL_VALUE,
    DESCRIPTION,
    NAME,
    PROVIDER,
    SERVICE,
)
from cli.infrastructure.command import async_command, authenticated_client
from alloy_runtime_types.dtos.organization_credentials import (
    CreateOrganizationCredentialRequest,
)
from alloy_runtime_types.enums.provider import Provider
from alloy_runtime_types.enums.tool_service import ToolService


def credentials_create_command(
    credential_type_id: str = typer.Option(
        ...,
        "--type",
        help="Credential type: org_provider or org_tool",
    ),
    name: str = NAME,
    plaintext_value: str = CREDENTIAL_VALUE,
    provider: Optional[str] = PROVIDER,
    service: Optional[str] = SERVICE,
    tool_id: Optional[str] = typer.Option(
        None,
        "--tool-id",
        help="Tool identifier (mutually exclusive with --provider/--service)",
    ),
    description: Optional[str] = DESCRIPTION,
    expires_at: Optional[str] = typer.Option(
        None,
        "--expires-at",
        help="Expiration ISO 8601 (2025-12-31T23:59:59Z)",
    ),
    configuration: Optional[str] = typer.Option(
        None,
        "--configuration",
        help="Type-specific config as JSON",
    ),
) -> None:
    """Create an organization credential (BYOK).

    Requires organization owner or admin role.

    SECURITY: Use env var to avoid shell history exposure:
        alloy credentials create --value "$OPENAI_API_KEY" ...

    Examples:

        # Provider credential
        alloy credentials create --type org_provider -n "OpenAI" --value "sk-..." -p openai

        # Tool credential
        alloy credentials create --type org_tool -n "Search" --value "key" --tool-id web_search
    """
    _execute_create(
        credential_type_id=credential_type_id,
        name=name,
        plaintext_value=plaintext_value,
        provider=provider,
        service=service,
        tool_id=tool_id,
        description=description,
        expires_at=expires_at,
        configuration=configuration,
    )


@async_command
async def _execute_create(
    credential_type_id: str,
    name: str,
    plaintext_value: str,
    provider: Optional[str],
    service: Optional[str],
    tool_id: Optional[str],
    description: Optional[str],
    expires_at: Optional[str],
    configuration: Optional[str],
) -> None:
    """Execute the credential creation operation.

    Args:
        credential_type_id: Credential type ID
        name: Friendly name
        plaintext_value: API key value
        provider: Optional provider key string
        service: Optional service key string
        tool_id: Optional tool identifier
        description: Optional description
        expires_at: Optional expiration datetime (ISO 8601 string)
        configuration: Optional configuration JSON string

    Raises:
        typer.BadParameter: If validation fails (provider, service, tool_id, XOR, datetime, JSON)
        ConfigurationError: If environment variables are missing
        AuthenticationError: If API key is invalid
        ForbiddenError: User lacks permission (not owner/admin)
        ValidationError: If request data is invalid
        ConflictError: If duplicate credential exists
        ServerError: If server returns 5xx error
    """
    has_provider = provider is not None
    has_service = service is not None
    has_tool_id = tool_id is not None

    if sum((has_provider, has_service, has_tool_id)) > 1:
        raise typer.BadParameter(
            "Cannot combine --provider, --service, and --tool-id. Choose exactly one association flag."
        )

    # Validate and convert provider string to enum if provided
    provider_enum: Optional[Provider] = None
    if provider:
        provider_enum = validate_provider(provider)

    service_enum: Optional[ToolService] = None
    if service:
        try:
            service_enum = ToolService(service)
        except ValueError as exc:
            valid_services = ", ".join(item.value for item in ToolService)
            raise typer.BadParameter(
                f"Invalid service '{service}'. Valid services: {valid_services}"
            ) from exc

    # Parse expires_at if provided
    expires_datetime: Optional[datetime] = None
    if expires_at:
        try:
            expires_datetime = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
        except ValueError:
            raise typer.BadParameter(
                f"Invalid datetime format: '{expires_at}'. Must be ISO 8601 format (e.g., '2025-12-31T23:59:59Z')"
            )

    # Parse configuration JSON if provided
    configuration_dict: Optional[dict[str, Any]] = None
    if configuration:
        try:
            configuration_dict = json.loads(configuration)
            if not isinstance(configuration_dict, dict):
                raise typer.BadParameter(
                    f"Configuration must be a JSON object, got {type(configuration_dict).__name__}"
                )
        except json.JSONDecodeError as e:
            raise typer.BadParameter(f"Invalid JSON in --configuration: {e}")

    # Build request
    request = CreateOrganizationCredentialRequest(
        credential_type_id=credential_type_id,
        name=name,
        secret=plaintext_value,
        provider_key=provider_enum,
        service_key=service_enum,
        tool_id=tool_id,
        description=description,
        expires_at=expires_datetime,
        provider_config=configuration_dict,
    )

    # Execute API call with authenticated client
    async with authenticated_client() as (_config, client):
        response = await client.create_organization_credential(request)

    # Present success output
    present_org_credential_create_success(response)
