// src/services/grpc_client.rs

use common::proto::komp_ac::common::Empty;
use common::proto::komp_ac::table_structure::table_structure_service_client::TableStructureServiceClient;
use common::proto::komp_ac::table_structure::{GetTableStructureRequest, TableStructureResponse};
use common::proto::komp_ac::table_definition::{
    table_definition_client::TableDefinitionClient,
    PostTableDefinitionRequest, ProfileTreeResponse, TableDefinitionResponse,
};
use common::proto::komp_ac::table_script::{
    table_script_client::TableScriptClient,
    PostTableScriptRequest, TableScriptResponse,
};
use common::proto::komp_ac::tables_data::{
    tables_data_client::TablesDataClient,
    GetTableDataByPositionRequest,
    GetTableDataRequest,          // ADD THIS
    GetTableDataResponse,
    DeleteTableDataRequest,       // ADD THIS
    DeleteTableDataResponse,      // ADD THIS
    GetTableDataCountRequest,
    PostTableDataRequest, PostTableDataResponse, PutTableDataRequest,
    PutTableDataResponse,
};
use common::proto::komp_ac::search::{
    searcher_client::SearcherClient, SearchRequest, SearchResponse,
};
use anyhow::{Context, Result};
use std::collections::HashMap;
use tonic::transport::Channel;
use prost_types::Value;

#[derive(Clone)]
pub struct GrpcClient {
    table_structure_client: TableStructureServiceClient<Channel>,
    table_definition_client: TableDefinitionClient<Channel>,
    table_script_client: TableScriptClient<Channel>,
    tables_data_client: TablesDataClient<Channel>,
    search_client: SearcherClient<Channel>,
}

impl GrpcClient {
    pub async fn new() -> Result<Self> {
        let channel = Channel::from_static("http://[::1]:50051")
            .connect()
            .await
            .context("Failed to create gRPC channel")?;

        let table_structure_client =
            TableStructureServiceClient::new(channel.clone());
        let table_definition_client =
            TableDefinitionClient::new(channel.clone());
        let table_script_client = TableScriptClient::new(channel.clone());
        let tables_data_client = TablesDataClient::new(channel.clone());
        let search_client = SearcherClient::new(channel.clone());

        Ok(Self {
            table_structure_client,
            table_definition_client,
            table_script_client,
            tables_data_client,
            search_client,
        })
    }

    pub async fn get_table_structure(
        &mut self,
        profile_name: String,
        table_name: String,
    ) -> Result<TableStructureResponse> {
        let grpc_request = GetTableStructureRequest {
            profile_name,
            table_name,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .table_structure_client
            .get_table_structure(request)
            .await
            .context("gRPC GetTableStructure call failed")?;
        Ok(response.into_inner())
    }

    pub async fn get_profile_tree(
        &mut self,
    ) -> Result<ProfileTreeResponse> {
        let request = tonic::Request::new(Empty::default());
        let response = self
            .table_definition_client
            .get_profile_tree(request)
            .await
            .context("gRPC GetProfileTree call failed")?;
        Ok(response.into_inner())
    }

    pub async fn post_table_definition(
        &mut self,
        request: PostTableDefinitionRequest,
    ) -> Result<TableDefinitionResponse> {
        let tonic_request = tonic::Request::new(request);
        let response = self
            .table_definition_client
            .post_table_definition(tonic_request)
            .await
            .context("gRPC PostTableDefinition call failed")?;
        Ok(response.into_inner())
    }

    pub async fn post_table_script(
        &mut self,
        request: PostTableScriptRequest,
    ) -> Result<TableScriptResponse> {
        let tonic_request = tonic::Request::new(request);
        let response = self
            .table_script_client
            .post_table_script(tonic_request)
            .await
            .context("gRPC PostTableScript call failed")?;
        Ok(response.into_inner())
    }

    // Existing TablesData methods
    pub async fn get_table_data_count(
        &mut self,
        profile_name: String,
        table_name: String,
    ) -> Result<u64> {
        let grpc_request = GetTableDataCountRequest {
            profile_name,
            table_name,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .get_table_data_count(request)
            .await
            .context("gRPC GetTableDataCount call failed")?;
        Ok(response.into_inner().count as u64)
    }

    pub async fn get_table_data_by_position(
        &mut self,
        profile_name: String,
        table_name: String,
        position: i32,
    ) -> Result<GetTableDataResponse> {
        let grpc_request = GetTableDataByPositionRequest {
            profile_name,
            table_name,
            position,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .get_table_data_by_position(request)
            .await
            .context("gRPC GetTableDataByPosition call failed")?;
        Ok(response.into_inner())
    }

    // ADD THIS: Missing get_table_data method
    pub async fn get_table_data(
        &mut self,
        profile_name: String,
        table_name: String,
        id: i64,
    ) -> Result<GetTableDataResponse> {
        let grpc_request = GetTableDataRequest {
            profile_name,
            table_name,
            id,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .get_table_data(request)
            .await
            .context("gRPC GetTableData call failed")?;
        Ok(response.into_inner())
    }

    // ADD THIS: Missing delete_table_data method
    pub async fn delete_table_data(
        &mut self,
        profile_name: String,
        table_name: String,
        record_id: i64,
    ) -> Result<DeleteTableDataResponse> {
        let grpc_request = DeleteTableDataRequest {
            profile_name,
            table_name,
            record_id,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .delete_table_data(request)
            .await
            .context("gRPC DeleteTableData call failed")?;
        Ok(response.into_inner())
    }

    pub async fn post_table_data(
        &mut self,
        profile_name: String,
        table_name: String,
        data: HashMap<String, Value>,
    ) -> Result<PostTableDataResponse> {
        let grpc_request = PostTableDataRequest {
            profile_name,
            table_name,
            data,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .post_table_data(request)
            .await
            .context("gRPC PostTableData call failed")?;
        Ok(response.into_inner())
    }

    pub async fn put_table_data(
        &mut self,
        profile_name: String,
        table_name: String,
        id: i64,
        data: HashMap<String, Value>,
    ) -> Result<PutTableDataResponse> {
        let grpc_request = PutTableDataRequest {
            profile_name,
            table_name,
            id,
            data,
        };
        let request = tonic::Request::new(grpc_request);
        let response = self
            .tables_data_client
            .put_table_data(request)
            .await
            .context("gRPC PutTableData call failed")?;
        Ok(response.into_inner())
    }

    pub async fn search_table(
        &mut self,
        table_name: String,
        query: String,
    ) -> Result<SearchResponse> {
        let request = tonic::Request::new(SearchRequest { table_name, query });
        let response = self
            .search_client
            .search_table(request)
            .await?;
        Ok(response.into_inner())
    }
}
