"""Manager-side client for CyberLicensing operations."""

from __future__ import annotations

from datetime import datetime, timedelta
from typing import Dict, List, Optional

import requests

from .exceptions import ApiError, AuthenticationError


class ManagerClient:
    def __init__(self, base_url: str, token: Optional[str] = None, timeout: float = 5.0):
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.token = token

    # --- Auth helpers ---
    def authenticate(self, username: str, password: str) -> str:
        response = requests.post(
            f"{self.base_url}/api/login",
            json={"username": username, "password": password},
            timeout=self.timeout,
        )
        if response.status_code != 200:
            raise AuthenticationError("Invalid credentials")
        token = response.json().get("access_token")
        if not token:
            raise AuthenticationError("Unable to retrieve access token")
        self.token = token
        return token

    def _headers(self) -> Dict[str, str]:
        if not self.token:
            raise AuthenticationError("JWT token missing. Call authenticate() first or set token.")
        return {"Authorization": f"Bearer {self.token}"}

    def _request(self, method: str, path: str, require_auth: bool = True, **kwargs):
        headers = kwargs.pop('headers', {})
        if require_auth:
            headers.update(self._headers())
        response = requests.request(
            method,
            f"{self.base_url}{path}",
            headers=headers,
            timeout=self.timeout,
            **kwargs,
        )
        if response.status_code >= 400:
            raise ApiError(response.status_code, response.text, response.json() if response.content else None)
        if response.content:
            return response.json()
        return None

    # --- Project helpers ---
    def list_projects(self) -> List[Dict]:
        return self._request('GET', '/api/projects')

    def create_project(self, name: str) -> Dict:
        return self._request('POST', '/api/projects', json={"name": name})

    def update_metadata_schema(self, project_id: int, fields: List[Dict]) -> Dict:
        return self._request('PUT', f'/api/projects/{project_id}/metadata_fields', json={"fields": fields})

    def list_licenses(self, project_id: int) -> List[Dict]:
        return self._request('GET', f'/api/projects/{project_id}/licenses')

    def create_license(
        self,
        project_id: int,
        days_valid: Optional[int] = None,
        metadata: Optional[Dict[str, str]] = None,
    ) -> Dict:
        payload: Dict[str, object] = {}
        if days_valid is not None:
            payload['days_valid'] = days_valid
        if metadata is not None:
            payload['metadata'] = metadata
        return self._request('POST', f'/api/projects/{project_id}/licenses', json=payload)

    def update_license(
        self,
        license_id: int,
        *,
        is_active: Optional[bool] = None,
        expires_at: Optional[str] = None,
        reset_hwid: bool = False,
        metadata: Optional[Dict[str, str]] = None,
    ) -> Dict:
        payload: Dict[str, object] = {}
        if is_active is not None:
            payload['is_active'] = is_active
        if expires_at is not None:
            payload['expires_at'] = expires_at
        if reset_hwid:
            payload['reset_hwid'] = True
        if metadata is not None:
            payload['metadata'] = metadata
        return self._request('PUT', f'/api/licenses/{license_id}', json=payload)

    def extend_license(self, project_id: int, license_id: int, days: int) -> Dict:
        """Convenience helper to push expiration forward by *days*."""

        licenses = self.list_licenses(project_id)
        license_data = next((lic for lic in licenses if lic['id'] == license_id), None)
        if not license_data:
            raise ApiError(404, f"License {license_id} not found in project {project_id}", {})

        expires_at = license_data.get('expires_at')
        if not expires_at:
            raise ApiError(400, "License is lifetime; set an explicit expiration first", {})

        try:
            current_exp = datetime.fromisoformat(expires_at)
        except ValueError as exc:
            raise ApiError(500, f"Invalid expiration format: {expires_at}", {"error": str(exc)})

        new_expiration = (current_exp + timedelta(days=days)).date().isoformat()
        return self.update_license(license_id, expires_at=new_expiration)

    def delete_license(self, license_id: int) -> Dict:
        return self._request('DELETE', f'/api/licenses/{license_id}')