"""
OpenAI Provisioning Provider
Creates API keys via OpenAI's admin API
"""

import json
import urllib.request
import urllib.error
from datetime import datetime
from typing import Any, Optional

from id_wispera.provisioning.provider import ProvisioningProviderBase
from id_wispera.provisioning.types import (
    ProviderCapabilities,
    ProvisioningRequest,
    ProvisioningResult,
    RotationResult,
)


OPENAI_BASE_URL = "https://api.openai.com/v1"


class OpenAIProvider(ProvisioningProviderBase):
    """OpenAI provisioning provider using the admin API."""

    @property
    def provider(self) -> str:
        return "openai"

    @property
    def capabilities(self) -> ProviderCapabilities:
        return ProviderCapabilities(
            can_create=True,
            can_configure_permissions=True,
            can_rotate=True,
            can_revoke=True,
            can_list=True,
            supports_expiry=False,
            supports_scoped_keys=True,
        )

    def _get_api_key(self) -> str:
        """Get the API key from auth config."""
        if not self._auth or self._auth.get("type") != "api-key":
            raise ValueError("OpenAI provider requires api-key authentication (sk-admin-... key)")
        return self._auth["key"]

    def _make_request(
        self,
        method: str,
        url: str,
        data: Optional[dict] = None,
    ) -> tuple[int, Any]:
        """
        Make an HTTP request using urllib.

        Returns:
            Tuple of (status_code, response_body)
        """
        api_key = self._get_api_key()
        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }

        body = json.dumps(data).encode("utf-8") if data else None
        req = urllib.request.Request(url, data=body, headers=headers, method=method)

        try:
            with urllib.request.urlopen(req) as response:
                response_body = json.loads(response.read().decode("utf-8"))
                return response.status, response_body
        except urllib.error.HTTPError as e:
            error_body = e.read().decode("utf-8") if e.fp else "{}"
            try:
                return e.code, json.loads(error_body)
            except json.JSONDecodeError:
                return e.code, {"error": {"message": error_body}}

    def provision(self, request: ProvisioningRequest) -> ProvisioningResult:
        """Provision a new OpenAI API key."""
        self._ensure_initialized()

        config = request.config
        if config.get("provider") != "openai":
            return ProvisioningResult(
                success=False,
                error={
                    "code": "INVALID_CONFIG",
                    "message": f"Expected openai config, got {config.get('provider')}",
                },
            )

        if not self._auth or self._auth.get("type") != "api-key":
            return ProvisioningResult(
                success=False,
                error={
                    "code": "INVALID_AUTH",
                    "message": "OpenAI provider requires api-key authentication (sk-admin-... key)",
                },
            )

        try:
            key_type = config.get("keyType", "admin")
            if key_type == "service-account":
                return self._create_service_account_key(request, config)
            else:
                return self._create_admin_key(request, config)
        except Exception as e:
            return ProvisioningResult(
                success=False,
                error={
                    "code": "PROVISIONING_FAILED",
                    "message": f"OpenAI provisioning failed: {str(e)}",
                },
            )

    def list_keys(self) -> list[dict[str, Any]]:
        """List existing OpenAI admin API keys."""
        self._ensure_initialized()

        status, data = self._make_request(
            "GET",
            f"{OPENAI_BASE_URL}/organization/admin_api_keys",
        )

        if status < 200 or status >= 300:
            raise RuntimeError(f"Failed to list OpenAI keys: {status} {data}")

        keys = data.get("data", [])
        return [
            {
                "id": key["id"],
                "name": key["name"],
                "createdAt": datetime.utcfromtimestamp(key["created_at"]).isoformat(),
                "status": key.get("status", "active"),
            }
            for key in keys
        ]

    def rotate(self, existing_key_id: str, request: ProvisioningRequest) -> RotationResult:
        """Rotate an OpenAI API key."""
        new_credential = self.provision(request)

        return RotationResult(
            new_credential=new_credential,
            old_key_id=existing_key_id,
            recommended_revocation_delay=60,  # 1 minute for propagation
        )

    def revoke(self, key_id: str) -> dict[str, Any]:
        """Revoke an OpenAI API key."""
        self._ensure_initialized()

        try:
            status, data = self._make_request(
                "DELETE",
                f"{OPENAI_BASE_URL}/organization/admin_api_keys/{key_id}",
            )

            if status < 200 or status >= 300:
                return {"success": False, "error": f"Failed to revoke: {status} {data}"}

            return {"success": True}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def validate_auth(self) -> dict[str, Any]:
        """Validate OpenAI API key authentication."""
        self._ensure_initialized()

        try:
            status, _ = self._make_request(
                "GET",
                f"{OPENAI_BASE_URL}/organization/admin_api_keys?limit=1",
            )

            if 200 <= status < 300:
                return {"valid": True}

            return {"valid": False, "error": f"Auth validation failed: {status}"}
        except Exception as e:
            return {"valid": False, "error": str(e)}

    def _create_service_account_key(
        self,
        request: ProvisioningRequest,
        config: dict[str, Any],
    ) -> ProvisioningResult:
        """Create a project-scoped service account key."""
        project_id = config.get("projectId")
        if not project_id:
            return ProvisioningResult(
                success=False,
                error={
                    "code": "MISSING_PROJECT_ID",
                    "message": "projectId is required for service-account key type",
                },
            )

        organization_id = config.get("organizationId", "")
        url = f"{OPENAI_BASE_URL}/organization/projects/{project_id}/service_accounts"

        status, data = self._make_request("POST", url, {"name": request.name})

        if status < 200 or status >= 300:
            error_msg = data.get("error", {}).get("message", f"HTTP {status}")
            return ProvisioningResult(
                success=False,
                error={
                    "code": f"OPENAI_{status}",
                    "message": error_msg,
                    "details": data,
                },
            )

        now = datetime.utcnow().isoformat()
        api_key = data.get("api_key", {})
        human_owner = request.human_owner
        agent_id = request.agent_id

        return ProvisioningResult(
            success=True,
            credential_value=api_key.get("value"),
            provider_key_id=api_key.get("id"),
            metadata={
                "serviceAccountId": data.get("id"),
                "serviceAccountName": data.get("name"),
                "role": data.get("role"),
                "projectId": project_id,
                "organizationId": organization_id,
            },
            passport_input={
                "name": request.name,
                "credentialType": "api-key",
                "credentialValue": api_key.get("value"),
                "visaType": self.map_to_visa_type("openai", False),
                "issuingAuthority": "OpenAI",
                "platforms": [self.map_to_platform("openai")],
                "scope": ["inference"],
                "validFrom": now,
                "humanOwner": human_owner,
                "agentId": agent_id,
                "delegationChain": (
                    [h.model_dump(by_alias=True, mode="json") for h in request.delegation_chain]
                    if request.delegation_chain else [
                        {
                            "from": human_owner,
                            "to": agent_id or request.name,
                            "grantedAt": now,
                            "scope": ["inference"],
                            "notes": f"Provisioned via OpenAI API (project: {project_id})",
                        }
                    ]
                ),
                "tags": list(request.tags or []) + ["provisioned", "openai", "service-account"],
                "notes": request.notes or f"OpenAI service account key for project {project_id}",
            },
        )

    def _create_admin_key(
        self,
        request: ProvisioningRequest,
        config: dict[str, Any],
    ) -> ProvisioningResult:
        """Create an organization-level admin key."""
        organization_id = config.get("organizationId", "")
        url = f"{OPENAI_BASE_URL}/organization/admin_api_keys"

        status, data = self._make_request("POST", url, {"name": request.name})

        if status < 200 or status >= 300:
            error_msg = data.get("error", {}).get("message", f"HTTP {status}")
            return ProvisioningResult(
                success=False,
                error={
                    "code": f"OPENAI_{status}",
                    "message": error_msg,
                    "details": data,
                },
            )

        now = datetime.utcnow().isoformat()
        api_key = data.get("api_key", {})
        human_owner = request.human_owner
        agent_id = request.agent_id

        return ProvisioningResult(
            success=True,
            credential_value=api_key.get("value"),
            provider_key_id=api_key.get("id"),
            metadata={
                "adminKeyId": data.get("id"),
                "adminKeyName": data.get("name"),
                "organizationId": organization_id,
            },
            passport_input={
                "name": request.name,
                "credentialType": "api-key",
                "credentialValue": api_key.get("value"),
                "visaType": self.map_to_visa_type("openai", True),
                "issuingAuthority": "OpenAI",
                "platforms": [self.map_to_platform("openai")],
                "scope": ["admin", "inference", "organization"],
                "validFrom": now,
                "humanOwner": human_owner,
                "agentId": agent_id,
                "delegationChain": (
                    [h.model_dump(by_alias=True, mode="json") for h in request.delegation_chain]
                    if request.delegation_chain else [
                        {
                            "from": human_owner,
                            "to": agent_id or request.name,
                            "grantedAt": now,
                            "scope": ["admin", "inference", "organization"],
                            "notes": f"Provisioned via OpenAI Admin API (org: {organization_id})",
                        }
                    ]
                ),
                "tags": list(request.tags or []) + ["provisioned", "openai", "admin"],
                "notes": request.notes or f"OpenAI admin key for organization {organization_id}",
            },
        )
