use crate::proto::guardian_server::{Guardian, GuardianServer};
use crate::proto::{
    AccountState, ConfigureRequest, ConfigureResponse, DeltaObject as ProtoDeltaObject,
    GetAccountByKeyCommitmentRequest, GetAccountByKeyCommitmentResponse, GetDeltaProposalRequest,
    GetDeltaProposalResponse, GetDeltaProposalsRequest, GetDeltaProposalsResponse, GetDeltaRequest,
    GetDeltaResponse, GetDeltaSinceRequest, GetDeltaSinceResponse, GetPubkeyRequest,
    GetStateRequest, GetStateResponse, PushDeltaProposalRequest, PushDeltaProposalResponse,
    PushDeltaRequest, PushDeltaResponse, SignDeltaProposalRequest, SignDeltaProposalResponse,
};
use std::net::SocketAddr;
use std::sync::{Arc, Mutex as StdMutex};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

#[derive(Default)]
pub struct MockGuardianService {
    get_pubkey_response: Arc<StdMutex<Option<Result<String, Status>>>>,
    configure_response: Arc<StdMutex<Option<Result<ConfigureResponse, Status>>>>,
    push_delta_proposal_response: Arc<StdMutex<Option<Result<PushDeltaProposalResponse, Status>>>>,
    get_delta_proposal_response: Arc<StdMutex<Option<Result<GetDeltaProposalResponse, Status>>>>,
    get_delta_proposals_response: Arc<StdMutex<Option<Result<GetDeltaProposalsResponse, Status>>>>,
    sign_delta_proposal_response: Arc<StdMutex<Option<Result<SignDeltaProposalResponse, Status>>>>,
    push_delta_response: Arc<StdMutex<Option<Result<PushDeltaResponse, Status>>>>,
    get_delta_response: Arc<StdMutex<Option<Result<GetDeltaResponse, Status>>>>,
    get_delta_since_response: Arc<StdMutex<Option<Result<GetDeltaSinceResponse, Status>>>>,
    get_state_response: Arc<StdMutex<Option<Result<GetStateResponse, Status>>>>,
    get_account_by_key_commitment_response:
        Arc<StdMutex<Option<Result<GetAccountByKeyCommitmentResponse, Status>>>>,
}

impl MockGuardianService {
    pub fn with_get_pubkey(self, response: Result<String, Status>) -> Self {
        *self.get_pubkey_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_configure(self, response: Result<ConfigureResponse, Status>) -> Self {
        *self.configure_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_push_delta_proposal(
        self,
        response: Result<PushDeltaProposalResponse, Status>,
    ) -> Self {
        *self.push_delta_proposal_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_delta_proposals(
        self,
        response: Result<GetDeltaProposalsResponse, Status>,
    ) -> Self {
        *self.get_delta_proposals_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_delta_proposal(
        self,
        response: Result<GetDeltaProposalResponse, Status>,
    ) -> Self {
        *self.get_delta_proposal_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_sign_delta_proposal(
        self,
        response: Result<SignDeltaProposalResponse, Status>,
    ) -> Self {
        *self.sign_delta_proposal_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_push_delta(self, response: Result<PushDeltaResponse, Status>) -> Self {
        *self.push_delta_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_delta(self, response: Result<GetDeltaResponse, Status>) -> Self {
        *self.get_delta_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_delta_since(self, response: Result<GetDeltaSinceResponse, Status>) -> Self {
        *self.get_delta_since_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_state(self, response: Result<GetStateResponse, Status>) -> Self {
        *self.get_state_response.lock().unwrap() = Some(response);
        self
    }

    pub fn with_get_account_by_key_commitment(
        self,
        response: Result<GetAccountByKeyCommitmentResponse, Status>,
    ) -> Self {
        *self.get_account_by_key_commitment_response.lock().unwrap() = Some(response);
        self
    }
}

#[tonic::async_trait]
impl Guardian for MockGuardianService {
    async fn get_pubkey(
        &self,
        _request: Request<GetPubkeyRequest>,
    ) -> Result<Response<crate::proto::GetPubkeyResponse>, Status> {
        let response = self
            .get_pubkey_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| Ok("mock_pubkey".to_string()));

        response.map(|pubkey| {
            Response::new(crate::proto::GetPubkeyResponse {
                pubkey,
                raw_pubkey: None,
            })
        })
    }

    async fn configure(
        &self,
        _request: Request<ConfigureRequest>,
    ) -> Result<Response<ConfigureResponse>, Status> {
        let response = self
            .configure_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(ConfigureResponse {
                    success: true,
                    message: String::new(),
                    ack_pubkey: "mock_ack_pubkey".to_string(),
                    ack_commitment: String::new(),
                })
            });

        response.map(Response::new)
    }

    async fn push_delta_proposal(
        &self,
        _request: Request<PushDeltaProposalRequest>,
    ) -> Result<Response<PushDeltaProposalResponse>, Status> {
        let response = self
            .push_delta_proposal_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(PushDeltaProposalResponse {
                    success: true,
                    message: String::new(),
                    commitment: "mock_commitment".to_string(),
                    delta: Some(create_mock_delta()),
                })
            });

        response.map(Response::new)
    }

    async fn get_delta_proposals(
        &self,
        _request: Request<GetDeltaProposalsRequest>,
    ) -> Result<Response<GetDeltaProposalsResponse>, Status> {
        let response = self
            .get_delta_proposals_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(GetDeltaProposalsResponse {
                    success: true,
                    message: String::new(),
                    proposals: vec![],
                })
            });

        response.map(Response::new)
    }

    async fn get_delta_proposal(
        &self,
        _request: Request<GetDeltaProposalRequest>,
    ) -> Result<Response<GetDeltaProposalResponse>, Status> {
        let response = self
            .get_delta_proposal_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(GetDeltaProposalResponse {
                    success: true,
                    message: String::new(),
                    proposal: Some(create_mock_delta()),
                })
            });

        response.map(Response::new)
    }

    async fn sign_delta_proposal(
        &self,
        _request: Request<SignDeltaProposalRequest>,
    ) -> Result<Response<SignDeltaProposalResponse>, Status> {
        let response = self
            .sign_delta_proposal_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(SignDeltaProposalResponse {
                    success: true,
                    message: String::new(),
                    delta: Some(create_mock_delta()),
                })
            });

        response.map(Response::new)
    }

    async fn push_delta(
        &self,
        _request: Request<PushDeltaRequest>,
    ) -> Result<Response<PushDeltaResponse>, Status> {
        let response = self
            .push_delta_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(PushDeltaResponse {
                    success: true,
                    message: String::new(),
                    delta: Some(create_mock_delta()),
                    ack_sig: None,
                })
            });

        response.map(Response::new)
    }

    async fn get_delta(
        &self,
        _request: Request<GetDeltaRequest>,
    ) -> Result<Response<GetDeltaResponse>, Status> {
        let response = self
            .get_delta_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(GetDeltaResponse {
                    success: true,
                    message: String::new(),
                    delta: Some(create_mock_delta()),
                })
            });

        response.map(Response::new)
    }

    async fn get_delta_since(
        &self,
        _request: Request<GetDeltaSinceRequest>,
    ) -> Result<Response<GetDeltaSinceResponse>, Status> {
        let response = self
            .get_delta_since_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(GetDeltaSinceResponse {
                    success: true,
                    message: String::new(),
                    merged_delta: Some(create_mock_delta()),
                })
            });

        response.map(Response::new)
    }

    async fn get_state(
        &self,
        _request: Request<GetStateRequest>,
    ) -> Result<Response<GetStateResponse>, Status> {
        let response = self
            .get_state_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| {
                Ok(GetStateResponse {
                    success: true,
                    message: String::new(),
                    state: Some(create_mock_account_state()),
                })
            });

        response.map(Response::new)
    }

    async fn get_account_by_key_commitment(
        &self,
        _request: Request<GetAccountByKeyCommitmentRequest>,
    ) -> Result<Response<GetAccountByKeyCommitmentResponse>, Status> {
        let response = self
            .get_account_by_key_commitment_response
            .lock()
            .unwrap()
            .take()
            .unwrap_or_else(|| Ok(GetAccountByKeyCommitmentResponse { accounts: vec![] }));

        response.map(Response::new)
    }
}

pub async fn start_mock_server(
    service: MockGuardianService,
) -> Result<String, Box<dyn std::error::Error>> {
    let addr: SocketAddr = "127.0.0.1:0".parse()?;
    let listener = tokio::net::TcpListener::bind(addr).await?;
    let local_addr = listener.local_addr()?;
    let endpoint = format!("http://{}", local_addr);

    tokio::spawn(async move {
        Server::builder()
            .add_service(GuardianServer::new(service))
            .serve_with_incoming(tokio_stream::wrappers::TcpListenerStream::new(listener))
            .await
            .ok();
    });

    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    Ok(endpoint)
}

pub fn create_mock_delta() -> ProtoDeltaObject {
    ProtoDeltaObject {
        account_id: "0x7bfb0f38b0fafa103f86a805594170".to_string(),
        nonce: 1,
        prev_commitment: "0x123".to_string(),
        delta_payload: r#"{"updates": []}"#.to_string(),
        new_commitment: "0x456".to_string(),
        ack_sig: String::new(),
        candidate_at: String::new(),
        canonical_at: None,
        discarded_at: None,
        status: None,
        ack_pubkey: None,
        ack_scheme: None,
    }
}

pub fn create_mock_account_state() -> AccountState {
    AccountState {
        account_id: "0x7bfb0f38b0fafa103f86a805594170".to_string(),
        state_json: r#"{"balance": 1000}"#.to_string(),
        commitment: "0x123".to_string(),
        created_at: "2024-01-01T00:00:00Z".to_string(),
        updated_at: "2024-01-01T00:00:00Z".to_string(),
    }
}
