"""
S3 / MinIO source connector.

Auth (env vars, never CLI args):
    AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN  (explicit keys)
    AWS_PROFILE                                                   (named profile)
    EXTRACTOR_S3_ENDPOINT_URL                                     (MinIO or custom)
    EXTRACTOR_S3_REGION                                           (optional override)

Examples:
    s3://my-bucket/prefix/
    s3://my-bucket/docs/report.pdf
    minio://my-bucket/data/  (alias for s3 with EXTRACTOR_S3_ENDPOINT_URL set)
"""

from __future__ import annotations

import contextlib
import fnmatch
import os
import tempfile
from collections.abc import Iterator
from pathlib import Path
from urllib.parse import urlparse

from .base import SourceFile
from .registry import register_source


class S3Source:
    scheme = "s3"

    def __init__(self) -> None:
        self._client = None

    def _get_client(self):
        if self._client is None:
            try:
                import boto3
            except ImportError as e:
                raise ImportError(
                    "boto3 not installed. Run: pip install 'coresdk-extractor[s3]'"
                ) from e
            kwargs = {}
            endpoint = os.environ.get("EXTRACTOR_S3_ENDPOINT_URL")
            if endpoint:
                kwargs["endpoint_url"] = endpoint
            region = os.environ.get("EXTRACTOR_S3_REGION")
            if region:
                kwargs["region_name"] = region
            profile = os.environ.get("AWS_PROFILE")
            if profile:
                session = boto3.Session(profile_name=profile)
                self._client = session.client("s3", **kwargs)
            else:
                self._client = boto3.client("s3", **kwargs)
        return self._client

    def list(self, uri: str, glob_pattern: str | None = None) -> Iterator[SourceFile]:
        parsed = urlparse(uri)
        bucket = parsed.netloc
        prefix = parsed.path.lstrip("/")
        client = self._get_client()
        paginator = client.get_paginator("list_objects_v2")
        for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
            for obj in page.get("Contents", []):
                key = obj["Key"]
                filename = key.split("/")[-1]
                if not filename:
                    continue  # skip directory markers
                file_uri = f"s3://{bucket}/{key}"
                if glob_pattern and not fnmatch.fnmatch(filename, glob_pattern):
                    continue
                yield SourceFile(
                    uri=file_uri,
                    filename=filename,
                    size_bytes=obj.get("Size"),
                    etag=obj.get("ETag", "").strip('"'),
                    version_id=obj.get("VersionId"),
                    last_modified=obj["LastModified"].isoformat()
                    if obj.get("LastModified")
                    else None,
                    content_type=obj.get("ContentType"),
                )

    @contextlib.contextmanager
    def fetch(self, source_file: SourceFile, tmp_dir: Path | None = None) -> Iterator[Path]:
        parsed = urlparse(source_file.uri)
        bucket = parsed.netloc
        key = parsed.path.lstrip("/")
        client = self._get_client()
        suffix = Path(source_file.filename).suffix or ".bin"
        with tempfile.NamedTemporaryFile(suffix=suffix, dir=tmp_dir, delete=False) as tmp:
            tmp_path = Path(tmp.name)
        try:
            client.download_file(bucket, key, str(tmp_path))
            yield tmp_path
        finally:
            with contextlib.suppress(FileNotFoundError):
                tmp_path.unlink()


# MinIO is S3 with endpoint_url — register under both schemes
class MinIOSource(S3Source):
    scheme = "minio"


register_source(S3Source())
register_source(MinIOSource())
