//! CreditService client library
//!
//! Provides a convenient client for interacting with CreditService.

use creditservice_proto::credit_service_client::CreditServiceClient;
use tonic::transport::Channel;
use tracing::debug;

pub use creditservice_proto::*;

/// CreditService client
pub struct Client {
    inner: CreditServiceClient<Channel>,
}

impl Client {
    /// Connect to a CreditService server
    pub async fn connect(addr: impl AsRef<str>) -> Result<Self, tonic::transport::Error> {
        let addr = addr.as_ref().to_string();
        debug!("Connecting to CreditService at {}", addr);
        let inner = CreditServiceClient::connect(addr).await?;
        Ok(Self { inner })
    }

    /// Get wallet for a project
    pub async fn get_wallet(
        &mut self,
        project_id: impl Into<String>,
    ) -> Result<Wallet, tonic::Status> {
        let request = GetWalletRequest {
            project_id: project_id.into(),
        };
        let response = self.inner.get_wallet(request).await?;
        response
            .into_inner()
            .wallet
            .ok_or_else(|| tonic::Status::not_found("Wallet not found"))
    }

    /// Create a new wallet
    pub async fn create_wallet(
        &mut self,
        project_id: impl Into<String>,
        org_id: impl Into<String>,
        initial_balance: i64,
    ) -> Result<Wallet, tonic::Status> {
        let request = CreateWalletRequest {
            project_id: project_id.into(),
            org_id: org_id.into(),
            initial_balance,
        };
        let response = self.inner.create_wallet(request).await?;
        response
            .into_inner()
            .wallet
            .ok_or_else(|| tonic::Status::internal("Failed to create wallet"))
    }

    /// Check quota before resource creation
    pub async fn check_quota(
        &mut self,
        project_id: impl Into<String>,
        resource_type: ResourceType,
        quantity: i32,
        estimated_cost: i64,
    ) -> Result<CheckQuotaResponse, tonic::Status> {
        let request = CheckQuotaRequest {
            project_id: project_id.into(),
            resource_type: resource_type as i32,
            quantity,
            estimated_cost,
        };
        self.inner.check_quota(request).await.map(|r| r.into_inner())
    }

    /// Reserve credits for a resource creation
    pub async fn reserve_credits(
        &mut self,
        project_id: impl Into<String>,
        amount: i64,
        description: impl Into<String>,
        resource_type: impl Into<String>,
        ttl_seconds: i32,
    ) -> Result<Reservation, tonic::Status> {
        let request = ReserveCreditsRequest {
            project_id: project_id.into(),
            amount,
            description: description.into(),
            resource_type: resource_type.into(),
            ttl_seconds,
        };
        let response = self.inner.reserve_credits(request).await?;
        response
            .into_inner()
            .reservation
            .ok_or_else(|| tonic::Status::internal("Failed to create reservation"))
    }

    /// Commit a reservation after successful resource creation
    pub async fn commit_reservation(
        &mut self,
        reservation_id: impl Into<String>,
        actual_amount: i64,
        resource_id: impl Into<String>,
    ) -> Result<CommitReservationResponse, tonic::Status> {
        let request = CommitReservationRequest {
            reservation_id: reservation_id.into(),
            actual_amount,
            resource_id: resource_id.into(),
        };
        self.inner
            .commit_reservation(request)
            .await
            .map(|r| r.into_inner())
    }

    /// Release a reservation (e.g., if resource creation failed)
    pub async fn release_reservation(
        &mut self,
        reservation_id: impl Into<String>,
        reason: impl Into<String>,
    ) -> Result<bool, tonic::Status> {
        let request = ReleaseReservationRequest {
            reservation_id: reservation_id.into(),
            reason: reason.into(),
        };
        let response = self.inner.release_reservation(request).await?;
        Ok(response.into_inner().success)
    }
}
