// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::NodeService;
use crate::storage::storage_api::rpc_consumer::node_service::contract::bucket::{
    BucketOptions, DeleteBucketOptions, MakeBucketOptions,
};
use crate::storage::storage_api::rpc_consumer::node_service::{
    DiskError, StoragePeerS3ClientExt as _, decode_heal_bucket_rpc_options, reload_bucket_metadata, remove_bucket_metadata,
};
use rustfs_protos::proto_gen::node_service::*;
use tonic::{Request, Response, Status};
use tracing::debug;

impl NodeService {
    pub(super) async fn handle_delete_bucket_metadata(
        &self,
        request: Request<DeleteBucketMetadataRequest>,
    ) -> Result<Response<DeleteBucketMetadataResponse>, Status> {
        let request = request.into_inner();
        let bucket = request.bucket;
        if bucket.is_empty() {
            return Ok(Response::new(DeleteBucketMetadataResponse {
                success: false,
                error_info: Some("bucket name is missing".to_string()),
            }));
        }

        // Drop the bucket's cached metadata on this peer so it stops serving
        // stale configuration after the bucket has been deleted.
        match remove_bucket_metadata(&bucket).await {
            Ok(_) => Ok(Response::new(DeleteBucketMetadataResponse {
                success: true,
                error_info: None,
            })),
            Err(err) => Ok(Response::new(DeleteBucketMetadataResponse {
                success: false,
                error_info: Some(err.to_string()),
            })),
        }
    }

    pub(super) async fn handle_load_bucket_metadata(
        &self,
        request: Request<LoadBucketMetadataRequest>,
    ) -> Result<Response<LoadBucketMetadataResponse>, Status> {
        let request = request.into_inner();
        let scanner_maintenance_change = request.scanner_maintenance_change;
        let bucket = request.bucket;
        if bucket.is_empty() {
            return Ok(Response::new(LoadBucketMetadataResponse {
                error_code: None,
                success: false,
                error_info: Some("bucket name is missing".to_string()),
            }));
        }

        let Some(store) = self.resolve_object_store() else {
            return Ok(Response::new(LoadBucketMetadataResponse {
                success: false,
                error_info: Some("errServerNotInitialized".to_string()),
                error_code: Some(ControlPlaneErrorCode::ControlPlaneErrorNotInitialized as i32),
            }));
        };

        match reload_bucket_metadata(store, &bucket).await {
            Ok(()) => {
                if scanner_maintenance_change {
                    rustfs_scanner::record_scanner_maintenance_change(&bucket);
                }
                Ok(Response::new(LoadBucketMetadataResponse {
                    error_code: None,
                    success: true,
                    error_info: None,
                }))
            }
            Err(err) => Ok(Response::new(LoadBucketMetadataResponse {
                error_code: None,
                success: false,
                error_info: Some(err.to_string()),
            })),
        }
    }

    pub(super) async fn handle_delete_bucket(
        &self,
        request: Request<DeleteBucketRequest>,
    ) -> Result<Response<DeleteBucketResponse>, Status> {
        debug!("delete bucket");

        let request = request.into_inner();
        let options = if request.options.is_empty() {
            DeleteBucketOptions::default()
        } else {
            match serde_json::from_str::<DeleteBucketOptions>(&request.options) {
                Ok(options) => options,
                Err(err) => {
                    return Ok(Response::new(DeleteBucketResponse {
                        success: false,
                        error: Some(DiskError::other(format!("decode DeleteBucketOptions failed: {err}")).into()),
                    }));
                }
            }
        };
        match self.local_peer.delete_bucket(&request.bucket, &options).await {
            Ok(_) => Ok(Response::new(DeleteBucketResponse {
                success: true,
                error: None,
            })),
            Err(err) => Ok(Response::new(DeleteBucketResponse {
                success: false,
                error: Some(err.into()),
            })),
        }
    }

    pub(super) async fn handle_get_bucket_info(
        &self,
        request: Request<GetBucketInfoRequest>,
    ) -> Result<Response<GetBucketInfoResponse>, Status> {
        debug!("get bucket info");

        let request = request.into_inner();
        let options = match serde_json::from_str::<BucketOptions>(&request.options) {
            Ok(options) => options,
            Err(err) => {
                return Ok(Response::new(GetBucketInfoResponse {
                    success: false,
                    bucket_info: String::new(),
                    error: Some(DiskError::other(format!("decode BucketOptions failed: {err}")).into()),
                }));
            }
        };
        match self.local_peer.get_bucket_info(&request.bucket, &options).await {
            Ok(bucket_info) => {
                let bucket_info = match serde_json::to_string(&bucket_info) {
                    Ok(bucket_info) => bucket_info,
                    Err(err) => {
                        return Ok(Response::new(GetBucketInfoResponse {
                            success: false,
                            bucket_info: String::new(),
                            error: Some(DiskError::other(format!("encode data failed: {err}")).into()),
                        }));
                    }
                };

                Ok(Response::new(GetBucketInfoResponse {
                    success: true,
                    bucket_info,
                    error: None,
                }))
            }

            Err(err) => Ok(Response::new(GetBucketInfoResponse {
                success: false,
                bucket_info: String::new(),
                error: Some(err.into()),
            })),
        }
    }

    pub(super) async fn handle_make_bucket(
        &self,
        request: Request<MakeBucketRequest>,
    ) -> Result<Response<MakeBucketResponse>, Status> {
        debug!("make bucket");

        let request = request.into_inner();
        let options = match serde_json::from_str::<MakeBucketOptions>(&request.options) {
            Ok(options) => options,
            Err(err) => {
                return Ok(Response::new(MakeBucketResponse {
                    success: false,
                    error: Some(DiskError::other(format!("decode MakeBucketOptions failed: {err}")).into()),
                }));
            }
        };
        match self.local_peer.make_bucket(&request.name, &options).await {
            Ok(_) => Ok(Response::new(MakeBucketResponse {
                success: true,
                error: None,
            })),
            Err(err) => Ok(Response::new(MakeBucketResponse {
                success: false,
                error: Some(err.into()),
            })),
        }
    }

    pub(super) async fn handle_list_bucket(
        &self,
        request: Request<ListBucketRequest>,
    ) -> Result<Response<ListBucketResponse>, Status> {
        debug!("list bucket");

        let request = request.into_inner();
        let options = match serde_json::from_str::<BucketOptions>(&request.options) {
            Ok(options) => options,
            Err(err) => {
                return Ok(Response::new(ListBucketResponse {
                    success: false,
                    bucket_infos: Vec::new(),
                    error: Some(DiskError::other(format!("decode BucketOptions failed: {err}")).into()),
                }));
            }
        };
        match self.local_peer.list_bucket(&options).await {
            Ok(bucket_infos) => {
                let bucket_infos = bucket_infos
                    .into_iter()
                    .filter_map(|bucket_info| serde_json::to_string(&bucket_info).ok())
                    .collect();
                Ok(Response::new(ListBucketResponse {
                    success: true,
                    bucket_infos,
                    error: None,
                }))
            }

            Err(err) => Ok(Response::new(ListBucketResponse {
                success: false,
                bucket_infos: Vec::new(),
                error: Some(err.into()),
            })),
        }
    }

    pub(super) async fn handle_heal_bucket(
        &self,
        request: Request<HealBucketRequest>,
    ) -> Result<Response<HealBucketResponse>, Status> {
        debug!("heal bucket");
        let request = request.into_inner();
        let (options, fenced_pools) = match decode_heal_bucket_rpc_options(&request.options) {
            Ok(options) => options,
            Err(err) => {
                return Ok(Response::new(HealBucketResponse {
                    success: false,
                    error: Some(DiskError::other(format!("decode HealOpts failed: {err}")).into()),
                }));
            }
        };

        match self
            .local_peer
            .heal_bucket_with_fence(&request.bucket, &options, &fenced_pools)
            .await
        {
            Ok(_) => Ok(Response::new(HealBucketResponse {
                success: true,
                error: None,
            })),

            Err(err) => Ok(Response::new(HealBucketResponse {
                success: false,
                error: Some(err.into()),
            })),
        }
    }
}
