// Copyright 2024 RisingWave Labs
//
// 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 risingwave_common::catalog::{TableId, UserId};
pub use risingwave_common::id::SubscriptionId;
use risingwave_common::id::{DatabaseId, SchemaId};
use risingwave_common::util::epoch::Epoch;
use risingwave_pb::catalog::PbSubscription;
use risingwave_pb::catalog::subscription::PbSubscriptionState;

use super::OwnedByUserCatalog;
use crate::WithOptions;
use crate::error::{ErrorCode, Result};
use crate::handler::util::convert_interval_to_u64_seconds;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(test, derive(Default))]
pub struct SubscriptionCatalog {
    /// Id of the subscription. For debug now.
    pub id: SubscriptionId,

    /// Name of the subscription. For debug now.
    pub name: String,

    /// Full SQL definition of the subscription. For debug now.
    pub definition: String,

    /// The retention seconds of the subscription.
    pub retention_seconds: u64,

    /// The database id
    pub database_id: DatabaseId,

    /// The schema id
    pub schema_id: SchemaId,

    /// The subscription depends on the upstream list
    pub dependent_table_id: TableId,

    /// The user id
    pub owner: UserId,

    pub initialized_at_epoch: Option<Epoch>,
    pub created_at_epoch: Option<Epoch>,

    pub created_at_cluster_version: Option<String>,
    pub initialized_at_cluster_version: Option<String>,

    pub subscription_state: SubscriptionState,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum SubscriptionState {
    #[default]
    Init,
    Created,
}

impl SubscriptionCatalog {
    pub fn set_retention_seconds(&mut self, properties: &WithOptions) -> Result<()> {
        let retention_seconds_str = properties.get("retention").ok_or_else(|| {
            ErrorCode::InternalError("Subscription retention time not set.".to_owned())
        })?;
        let retention_seconds = convert_interval_to_u64_seconds(retention_seconds_str)?;
        self.retention_seconds = retention_seconds;
        Ok(())
    }

    pub fn create_sql(&self) -> String {
        self.definition.clone()
    }

    pub fn to_proto(&self) -> PbSubscription {
        PbSubscription {
            id: self.id,
            name: self.name.clone(),
            definition: self.definition.clone(),
            retention_seconds: self.retention_seconds,
            database_id: self.database_id,
            schema_id: self.schema_id,
            initialized_at_epoch: self.initialized_at_epoch.map(|e| e.0),
            created_at_epoch: self.created_at_epoch.map(|e| e.0),
            owner: self.owner,
            initialized_at_cluster_version: self.initialized_at_cluster_version.clone(),
            created_at_cluster_version: self.created_at_cluster_version.clone(),
            dependent_table_id: self.dependent_table_id,
            subscription_state: match self.subscription_state {
                SubscriptionState::Init => PbSubscriptionState::Init.into(),
                SubscriptionState::Created => PbSubscriptionState::Created.into(),
            },
        }
    }
}

impl From<&PbSubscription> for SubscriptionCatalog {
    fn from(prost: &PbSubscription) -> Self {
        Self {
            id: prost.id,
            name: prost.name.clone(),
            definition: prost.definition.clone(),
            retention_seconds: prost.retention_seconds,
            database_id: prost.database_id,
            schema_id: prost.schema_id,
            dependent_table_id: prost.dependent_table_id,
            owner: prost.owner,
            created_at_epoch: prost.created_at_epoch.map(Epoch::from),
            initialized_at_epoch: prost.initialized_at_epoch.map(Epoch::from),
            created_at_cluster_version: prost.created_at_cluster_version.clone(),
            initialized_at_cluster_version: prost.initialized_at_cluster_version.clone(),
            subscription_state: match PbSubscriptionState::try_from(prost.subscription_state)
                .unwrap()
            {
                PbSubscriptionState::Init => SubscriptionState::Init,
                PbSubscriptionState::Created => SubscriptionState::Created,
                PbSubscriptionState::Unspecified => unreachable!(),
            },
        }
    }
}

impl OwnedByUserCatalog for SubscriptionCatalog {
    fn owner(&self) -> UserId {
        self.owner
    }
}
