use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;

pub type JsonValue = Value;

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeConfig {
    pub port: u16,
    pub provider_port: u16,
    pub provider_data_port: u16,
    pub database_url: String,
    pub identity: RuntimeIdentity,
    pub provider_token: String,
    pub heartbeat_timeout_ms: u64,
    pub task_pending_timeout_ms: u64,
    pub database_volume_name: String,
    pub database_volume_size: String,
    pub pgdata_backup_staging_dir: String,
    pub baidu_netdisk_internal_url: String,
    pub microservices: Vec<MicroserviceConfig>,
    pub log_file: String,
    pub database_pool_max: usize,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeIdentity {
    pub environment: String,
    pub namespace: String,
    pub database_name: String,
    pub deploy_ref: String,
    pub service_id: String,
    pub repo: String,
    pub commit: String,
    pub requested_commit: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceConfig {
    pub id: String,
    pub name: String,
    pub provider_id: String,
    pub description: String,
    pub repository: MicroserviceRepositoryConfig,
    pub backend: MicroserviceBackendConfig,
    #[serde(default)]
    pub deployment: MicroserviceDeploymentConfig,
    pub development: MicroserviceDevelopmentConfig,
    pub frontend: MicroserviceFrontendConfig,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceRepositoryConfig {
    pub url: String,
    pub commit_id: String,
    pub dockerfile: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub artifact_source: Option<ArtifactSourceConfig>,
    pub compose_file: String,
    pub compose_service: String,
    pub container_name: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtifactSourceConfig {
    pub kind: String,
    pub image_ref: String,
    pub digest_pin_required: bool,
    pub mirror_repository: String,
    pub ci_dockerfile_build: bool,
    pub pull_only_cd: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceBackendConfig {
    pub node_base_url: String,
    pub node_bind_host: String,
    pub node_port: u16,
    pub proxy_mode: String,
    pub frontend_only: bool,
    pub public: bool,
    pub allowed_methods: Vec<String>,
    pub allowed_path_prefixes: Vec<String>,
    pub health_path: String,
    pub timeout_ms: u64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceDeploymentConfig {
    pub mode: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub adapter_service_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub k3s_service_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub namespace: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub expected_node_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub active_node_id: Option<String>,
}

impl Default for MicroserviceDeploymentConfig {
    fn default() -> Self {
        Self {
            mode: "unidesk-direct".to_string(),
            adapter_service_id: None,
            k3s_service_id: None,
            namespace: None,
            expected_node_ids: None,
            active_node_id: None,
        }
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceDevelopmentConfig {
    pub provider_id: String,
    pub ssh_passthrough: bool,
    pub worktree_path: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MicroserviceFrontendConfig {
    pub route: String,
    pub integrated: bool,
}

#[derive(Debug, Clone)]
pub struct RawTaskRow {
    pub id: String,
    pub provider_id: String,
    pub command: String,
    pub status: String,
    pub payload: JsonValue,
    pub result: Option<JsonValue>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone)]
pub struct ScheduledTaskRow {
    pub id: String,
    pub name: String,
    pub description: String,
    pub enabled: bool,
    pub schedule_json: JsonValue,
    pub action_json: JsonValue,
    pub concurrency_policy: String,
    pub next_run_at: Option<DateTime<Utc>>,
    pub last_run_at: Option<DateTime<Utc>>,
    pub last_run_id: Option<String>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone)]
pub struct ScheduledTaskRunRow {
    pub id: String,
    pub schedule_id: String,
    pub trigger_type: String,
    pub status: String,
    pub task_id: Option<String>,
    pub result: Option<JsonValue>,
    pub error: Option<String>,
    pub started_at: Option<DateTime<Utc>>,
    pub finished_at: Option<DateTime<Utc>>,
    pub duration_ms: Option<i64>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestPerformanceSample {
    pub at: String,
    pub component: String,
    pub method: String,
    pub path: String,
    pub status: u16,
    pub duration_ms: f64,
    pub ok: bool,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OperationPerformanceSample {
    pub at: String,
    pub service: String,
    pub operation: String,
    pub duration_ms: f64,
    pub ok: bool,
    pub detail: String,
}

#[derive(Debug, Clone)]
pub struct MicroserviceProxyCacheEntry {
    pub expires_at_ms: i64,
    pub stale_expires_at_ms: i64,
    pub status: u16,
    pub content_type: String,
    pub body_text: String,
}
