from pathlib import Path

from app.core.config import Settings


class LocalFileStorage:
    def __init__(self, settings: Settings):
        self.settings = settings
        self.base_dir = Path(settings.upload_dir)

    def save(self, key: str, data: bytes) -> str:
        path = self.base_dir / key
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_bytes(data)
        return str(path)

    def read_bytes(self, storage_path: str) -> bytes:
        return Path(storage_path).read_bytes()

    def exists(self, storage_path: str) -> bool:
        return Path(storage_path).exists()

    def local_path(self, storage_path: str) -> Path | None:
        path = Path(storage_path)
        return path if path.exists() else None


class S3FileStorage:
    def __init__(self, settings: Settings):
        self.settings = settings
        import boto3

        self.bucket = settings.s3_bucket
        self.client = boto3.client(
            "s3",
            endpoint_url=settings.s3_endpoint_url or None,
            aws_access_key_id=settings.s3_access_key or None,
            aws_secret_access_key=settings.s3_secret_key or None,
            region_name=settings.s3_region or None,
        )

    def _object_key(self, key: str) -> str:
        prefix = (self.settings.s3_key_prefix or "").strip("/")
        return f"{prefix}/{key}" if prefix else key

    def save(self, key: str, data: bytes) -> str:
        object_key = self._object_key(key)
        self.client.put_object(Bucket=self.bucket, Key=object_key, Body=data, ContentType="application/pdf")
        return f"s3://{self.bucket}/{object_key}"

    def read_bytes(self, storage_path: str) -> bytes:
        bucket, key = self._parse_uri(storage_path)
        response = self.client.get_object(Bucket=bucket, Key=key)
        return response["Body"].read()

    def exists(self, storage_path: str) -> bool:
        from botocore.exceptions import ClientError

        bucket, key = self._parse_uri(storage_path)
        try:
            self.client.head_object(Bucket=bucket, Key=key)
            return True
        except ClientError:
            return False

    def local_path(self, storage_path: str) -> Path | None:
        return None

    @staticmethod
    def _parse_uri(storage_path: str) -> tuple[str, str]:
        if not storage_path.startswith("s3://"):
            raise ValueError("Invalid S3 storage path")
        without_scheme = storage_path[5:]
        bucket, _, key = without_scheme.partition("/")
        return bucket, key


def get_file_storage(settings: Settings | None = None):
    from app.core.config import get_settings

    settings = settings or get_settings()
    if settings.storage_backend == "s3":
        return S3FileStorage(settings)
    return LocalFileStorage(settings)


def read_storage_bytes(storage_path: str, settings: Settings | None = None) -> bytes:
    from app.core.config import get_settings

    settings = settings or get_settings()
    if storage_path.startswith("s3://"):
        return S3FileStorage(settings).read_bytes(storage_path)
    return Path(storage_path).read_bytes()
