"""Cloud Audio Locker: S3 upload/download for MP3s in Audio_Bucket."""

from __future__ import annotations

import json
import os
from pathlib import Path
from typing import TYPE_CHECKING, Any

from botocore.exceptions import BotoCoreError, ClientError

from alice_cli.constants import AUDIO_BUCKET_ENV_VAR, get_audio_bucket
from alice_cli.errors import AliceCLIError

if TYPE_CHECKING:
    from alice_cli.models import CLIConfig


def resolve_audio_bucket(
    config: CLIConfig,
    cli_override: str | None = None,
) -> str:
    """Resolve the Audio_Bucket name using the configuration precedence chain.

    Precedence: cli_override (--audio-bucket) > ALICE_AUDIO_BUCKET env var >
    ~/.alice/credentials.json audio_bucket field > service default.

    Args:
        config: CLI configuration (provides namespace, environment).
        cli_override: Optional --audio-bucket CLI option value.

    Returns:
        Resolved S3 bucket name.
    """
    # 1. CLI override
    if cli_override is not None:
        return cli_override

    # 2. Environment variable
    env_val = os.environ.get(AUDIO_BUCKET_ENV_VAR)
    if env_val:
        return env_val

    # 3. Stored credential (~/.alice/credentials.json)
    try:
        creds_path = Path("~/.alice/credentials.json").expanduser()
        creds = json.loads(creds_path.read_text())
        bucket: str | None = creds.get("audio_bucket")
        if bucket:
            return bucket
    except (FileNotFoundError, KeyError, json.JSONDecodeError, OSError):
        pass

    # 4. Service default
    return get_audio_bucket(config.namespace, config.environment)


def upload_audio(
    session: Any,
    bucket: str,
    key: str,
    audio_data: bytes,
) -> None:
    """Upload MP3 audio data to the Audio_Bucket.

    Args:
        session: Authenticated boto3 session.
        bucket: S3 bucket name.
        key: S3 object key.
        audio_data: MP3 bytes to upload.

    Raises:
        AliceCLIError: If the upload fails.
    """
    try:
        s3 = session.client("s3")
        s3.put_object(
            Bucket=bucket,
            Key=key,
            Body=audio_data,
            ContentType="audio/mpeg",
        )
    except (ClientError, BotoCoreError) as exc:
        raise AliceCLIError(f"Failed to upload audio to s3://{bucket}/{key}: {exc}") from exc


def download_audio(
    session: Any,
    bucket: str,
    key: str,
) -> bytes:
    """Download MP3 audio data from the Audio_Bucket.

    Args:
        session: Authenticated boto3 session.
        bucket: S3 bucket name.
        key: S3 object key.

    Returns:
        MP3 audio bytes.

    Raises:
        AliceCLIError: If the download fails.
    """
    try:
        s3 = session.client("s3")
        response = s3.get_object(Bucket=bucket, Key=key)
        data: bytes = response["Body"].read()
        return data
    except (ClientError, BotoCoreError) as exc:
        raise AliceCLIError(f"Failed to download audio from s3://{bucket}/{key}: {exc}") from exc


def publish_audio(
    session: Any,
    bucket: str,
    artifact_key: str,
    session_id: str,
    expires_hours: int | None = None,
) -> str:
    """Publish an MP3 to the share prefix or generate a presigned URL.

    If expires_hours is None, copies to published/teaparty/{session_id}.mp3
    with public-read ACL. If expires_hours is set, generates a presigned GET
    URL with the specified TTL.

    Args:
        session: Authenticated boto3 session.
        bucket: S3 bucket name.
        artifact_key: Source S3 key of the MP3.
        session_id: Session UUID for the published path.
        expires_hours: Optional TTL in hours for presigned URL.

    Returns:
        The published URL (direct HTTPS or presigned).

    Raises:
        AliceCLIError: If the publish operation fails.
    """
    s3 = session.client("s3")

    if expires_hours is not None:
        # Generate a presigned GET URL
        try:
            url: str = s3.generate_presigned_url(
                "get_object",
                Params={"Bucket": bucket, "Key": artifact_key},
                ExpiresIn=expires_hours * 3600,
            )
            return url
        except (ClientError, BotoCoreError) as exc:
            raise AliceCLIError(
                f"Failed to generate presigned URL for s3://{bucket}/{artifact_key}: {exc}"
            ) from exc

    # Permanent publish: copy to share prefix with public-read ACL
    published_key = f"published/teaparty/{session_id}.mp3"
    try:
        s3.copy_object(
            Bucket=bucket,
            CopySource={"Bucket": bucket, "Key": artifact_key},
            Key=published_key,
            ACL="public-read",
            ContentType="audio/mpeg",
        )
    except (ClientError, BotoCoreError) as exc:
        raise AliceCLIError(
            f"Failed to publish audio to s3://{bucket}/{published_key}: {exc}"
        ) from exc

    return f"https://{bucket}.s3.amazonaws.com/{published_key}"


def unpublish_audio(
    session: Any,
    bucket: str,
    session_id: str,
) -> None:
    """Remove an MP3 from the share prefix.

    Args:
        session: Authenticated boto3 session.
        bucket: S3 bucket name.
        session_id: Session UUID identifying the published file.

    Raises:
        AliceCLIError: If the unpublish operation fails.
    """
    published_key = f"published/teaparty/{session_id}.mp3"
    try:
        s3 = session.client("s3")
        s3.delete_object(Bucket=bucket, Key=published_key)
    except (ClientError, BotoCoreError) as exc:
        raise AliceCLIError(
            f"Failed to unpublish audio at s3://{bucket}/{published_key}: {exc}"
        ) from exc
