from __future__ import annotations

import base64
import hashlib
import hmac
import os
import time
from datetime import datetime, timezone
from typing import Any
from urllib.parse import quote, urlencode

from services.storage.aliyun_oss_signed_upload import _require_text, _validate_object_key


class AliyunOssSignedDownloadService:
    storage_provider = "aliyun_oss"

    def __init__(
        self,
        *,
        endpoint: str,
        bucket: str,
        region: str,
        access_key_id: str,
        access_key_secret: str,
        now_epoch: int | None = None,
    ) -> None:
        _require_text(endpoint, "ALIYUN_OSS_ENDPOINT")
        _require_text(bucket, "ALIYUN_OSS_BUCKET")
        _require_text(region, "ALIYUN_OSS_REGION")
        _require_text(access_key_id, "ALIYUN_OSS_ACCESS_KEY_ID")
        _require_text(access_key_secret, "ALIYUN_OSS_ACCESS_KEY_SECRET")
        self.endpoint = endpoint.removeprefix("https://").removeprefix("http://").strip("/")
        self.bucket = bucket.strip()
        self.storage_region = region.strip()
        self._access_key_id = access_key_id.strip()
        self._access_key_secret = access_key_secret.strip()
        self._now_epoch = now_epoch

    @classmethod
    def from_env(cls) -> "AliyunOssSignedDownloadService":
        missing = [
            name
            for name in (
                "ALIYUN_OSS_ENDPOINT",
                "ALIYUN_OSS_BUCKET",
                "ALIYUN_OSS_REGION",
                "ALIYUN_OSS_ACCESS_KEY_ID",
                "ALIYUN_OSS_ACCESS_KEY_SECRET",
            )
            if not os.environ.get(name, "").strip()
        ]
        if missing:
            raise ValueError("Missing OSS download configuration: " + ", ".join(missing))
        return cls(
            endpoint=os.environ["ALIYUN_OSS_ENDPOINT"],
            bucket=os.environ["ALIYUN_OSS_BUCKET"],
            region=os.environ["ALIYUN_OSS_REGION"],
            access_key_id=os.environ["ALIYUN_OSS_ACCESS_KEY_ID"],
            access_key_secret=os.environ["ALIYUN_OSS_ACCESS_KEY_SECRET"],
        )

    def create_download_url(self, *, object_key: str, expires_in_seconds: int) -> dict[str, Any]:
        _validate_object_key(object_key)
        if expires_in_seconds <= 0 or expires_in_seconds > 900:
            raise ValueError("expires_in_seconds must be between 1 and 900")
        now = self._now_epoch if self._now_epoch is not None else int(time.time())
        expires = now + expires_in_seconds
        canonical_resource = f"/{self.bucket}/{object_key}"
        string_to_sign = f"GET\n\n\n{expires}\n{canonical_resource}"
        signature = base64.b64encode(
            hmac.new(
                self._access_key_secret.encode("utf-8"),
                string_to_sign.encode("utf-8"),
                hashlib.sha1,
            ).digest()
        ).decode("ascii")
        query = urlencode(
            {
                "OSSAccessKeyId": self._access_key_id,
                "Expires": str(expires),
                "Signature": signature,
            }
        )
        return {
            "method": "GET",
            "url": f"https://{self.bucket}.{self.endpoint}/{quote(object_key, safe='/')}?{query}",
            "expires_at": datetime.fromtimestamp(expires, tz=timezone.utc)
            .replace(microsecond=0)
            .isoformat()
            .replace("+00:00", "Z"),
        }
