use crank_core::{
    Agent, AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AsyncJobHandle, AsyncJobId,
    AuthProfile, DescriptorId, ExecutionMode, ExportMode, InvitationToken, InvocationLevel,
    InvocationLog, InvocationSource, JobStatus, MembershipRole, Operation, OperationId,
    OperationSecurityLevel, OperationStatus, PlatformApiKey, Protocol, SampleId, Secret, SecretId,
    SecretVersion, StreamSession, StreamSessionId, StreamStatus, UsagePeriod, UsageRollup, User,
    UserSessionId, Workspace, WorkspaceId,
};
use crank_mapping::MappingSet;
use crank_schema::Schema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::OffsetDateTime;

macro_rules! define_registry_id {
    ($name:ident) => {
        #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
        pub struct $name(String);

        impl $name {
            pub fn new(value: impl Into<String>) -> Self {
                Self(value.into())
            }

            pub fn as_str(&self) -> &str {
                &self.0
            }
        }

        impl From<String> for $name {
            fn from(value: String) -> Self {
                Self(value)
            }
        }

        impl From<&str> for $name {
            fn from(value: &str) -> Self {
                Self(value.to_owned())
            }
        }
    };
}

define_registry_id!(YamlImportJobId);

pub type RegistryOperation = Operation<Schema, MappingSet>;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Page<T> {
    pub items: Vec<T>,
    pub total: u64,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceRecord {
    pub workspace: Workspace,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MembershipRecord {
    pub workspace_id: WorkspaceId,
    pub user: User,
    pub role: MembershipRole,
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceMembershipRecord {
    pub workspace: Workspace,
    pub role: MembershipRole,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AuthUserRecord {
    pub user: User,
    pub password_hash: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SessionRecord {
    pub session_id: UserSessionId,
    pub user: User,
    pub memberships: Vec<WorkspaceMembershipRecord>,
    pub current_workspace_id: Option<WorkspaceId>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InvitationRecord {
    pub invitation: InvitationToken,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PlatformApiKeyRecord {
    pub api_key: PlatformApiKey,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SecretRecord {
    pub secret: Secret,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SecretVersionRecord {
    pub secret_version: SecretVersion,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StreamSessionRecord {
    pub session: StreamSession,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AsyncJobRecord {
    pub job: AsyncJobHandle,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InvocationLogRecord {
    pub log: InvocationLog,
    pub operation_name: String,
    pub operation_display_name: String,
    pub agent_slug: Option<String>,
    pub agent_display_name: Option<String>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageRollupRecord {
    pub rollup: UsageRollup,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageTimelinePoint {
    #[serde(with = "time::serde::rfc3339")]
    pub bucket_start: OffsetDateTime,
    pub calls_ok: u64,
    pub calls_error: u64,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageOperationBreakdown {
    pub operation_id: OperationId,
    pub operation_name: String,
    pub operation_display_name: String,
    pub protocol: Protocol,
    pub calls_total: u64,
    pub calls_error: u64,
    pub p50_ms: u64,
    pub p95_ms: u64,
    pub p99_ms: u64,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageAgentBreakdown {
    pub agent_id: AgentId,
    pub agent_slug: String,
    pub agent_display_name: String,
    pub calls_total: u64,
    pub calls_error: u64,
    pub p50_ms: u64,
    pub p95_ms: u64,
    pub p99_ms: u64,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AgentSummary {
    pub id: AgentId,
    pub workspace_id: WorkspaceId,
    pub slug: String,
    pub display_name: String,
    pub description: String,
    pub status: AgentStatus,
    pub current_draft_version: u32,
    pub latest_published_version: Option<u32>,
    pub created_at: OffsetDateTime,
    pub updated_at: OffsetDateTime,
    pub published_at: Option<OffsetDateTime>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AgentVersionRecord {
    pub agent_id: AgentId,
    pub workspace_id: WorkspaceId,
    pub version: u32,
    pub status: AgentStatus,
    pub created_at: OffsetDateTime,
    pub snapshot: AgentVersion,
    pub bindings: Vec<AgentOperationBinding>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PublishedAgentTool {
    pub workspace_id: WorkspaceId,
    pub workspace_slug: String,
    pub agent_id: AgentId,
    pub agent_slug: String,
    pub operation: RegistryOperation,
    pub tool_name: String,
    pub tool_title: String,
    pub tool_description: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OperationSummary {
    pub id: OperationId,
    pub workspace_id: WorkspaceId,
    pub name: String,
    pub display_name: String,
    pub category: String,
    pub protocol: Protocol,
    pub security_level: OperationSecurityLevel,
    pub target_url: String,
    pub target_action: String,
    pub status: OperationStatus,
    pub current_draft_version: u32,
    pub latest_published_version: Option<u32>,
    pub created_at: OffsetDateTime,
    pub updated_at: OffsetDateTime,
    pub published_at: Option<OffsetDateTime>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct OperationUsageSummary {
    pub operation_id: OperationId,
    pub calls_today: u64,
    pub error_rate_pct: f64,
    pub avg_latency_ms: u64,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OperationAgentRef {
    pub operation_id: OperationId,
    pub agent_id: AgentId,
    pub agent_slug: String,
    pub display_name: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct OperationVersionRecord {
    pub operation_id: OperationId,
    pub workspace_id: WorkspaceId,
    pub version: u32,
    pub status: OperationStatus,
    pub change_note: Option<String>,
    pub created_at: OffsetDateTime,
    pub created_by: Option<String>,
    pub snapshot: RegistryOperation,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SampleKind {
    InputJson,
    OutputJson,
    YamlImportSource,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OperationSampleMetadata {
    pub id: SampleId,
    pub operation_id: OperationId,
    pub version: u32,
    pub sample_kind: SampleKind,
    pub storage_ref: String,
    pub content_type: String,
    pub file_name: Option<String>,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DescriptorKind {
    ProtoUpload,
    DescriptorSet,
    ReflectionSnapshot,
    WsdlUpload,
    XsdUpload,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DescriptorMetadata {
    pub id: DescriptorId,
    pub operation_id: Option<OperationId>,
    pub version: Option<u32>,
    pub descriptor_kind: DescriptorKind,
    pub storage_ref: String,
    pub source_name: Option<String>,
    pub package_index: Option<Value>,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum YamlImportJobStatus {
    Pending,
    Completed,
    Failed,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct YamlImportJob {
    pub id: YamlImportJobId,
    pub source_sample_id: Option<SampleId>,
    pub status: YamlImportJobStatus,
    pub format_version: String,
    pub mode: ExportMode,
    pub result_operation_id: Option<OperationId>,
    pub result_version: Option<u32>,
    pub error_text: Option<String>,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
    #[serde(with = "time::serde::rfc3339::option")]
    pub finished_at: Option<OffsetDateTime>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct YamlImportJobCompletion {
    pub status: YamlImportJobStatus,
    pub result_operation_id: Option<OperationId>,
    pub result_version: Option<u32>,
    pub error_text: Option<String>,
    #[serde(with = "time::serde::rfc3339")]
    pub finished_at: OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateInvocationLogRequest<'a> {
    pub log: &'a InvocationLog,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ListInvocationLogsQuery<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub level: Option<InvocationLevel>,
    pub search_text: Option<&'a str>,
    pub source: Option<InvocationSource>,
    pub operation_id: Option<&'a OperationId>,
    pub agent_id: Option<&'a AgentId>,
    pub created_after: Option<&'a str>,
    pub limit: u32,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UsageBucket {
    Hour,
    Day,
    Week,
    Month,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UsageQuery<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub period: UsagePeriod,
    pub source: Option<InvocationSource>,
    pub created_after: &'a str,
    pub bucket: UsageBucket,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UsageSummary {
    pub rollup: UsageRollup,
    pub success_rate: f64,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateVersionRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub snapshot: &'a RegistryOperation,
    pub change_note: Option<&'a str>,
    pub created_by: Option<&'a str>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PublishRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub operation_id: &'a OperationId,
    pub version: u32,
    pub published_at: &'a OffsetDateTime,
    pub published_by: Option<&'a str>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CreateYamlImportJobRequest<'a> {
    pub id: &'a YamlImportJobId,
    pub source_sample_id: Option<&'a SampleId>,
    pub format_version: &'a str,
    pub mode: ExportMode,
    pub created_at: &'a OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SaveAuthProfileRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub profile: &'a AuthProfile,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateWorkspaceRequest<'a> {
    pub workspace: &'a Workspace,
}

#[derive(Clone, Debug, PartialEq)]
pub struct UpdateWorkspaceRequest<'a> {
    pub workspace: &'a Workspace,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateAgentRequest<'a> {
    pub agent: &'a Agent,
    pub version: &'a AgentVersion,
    pub bindings: &'a [AgentOperationBinding],
}

#[derive(Clone, Debug, PartialEq)]
pub struct SaveAgentBindingsRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub agent_id: &'a AgentId,
    pub agent_version: u32,
    pub bindings: &'a [AgentOperationBinding],
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PublishAgentRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub agent_id: &'a AgentId,
    pub version: u32,
    pub published_at: &'a OffsetDateTime,
    pub published_by: Option<&'a str>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CreateInvitationRequest<'a> {
    pub invitation: &'a InvitationToken,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreatePlatformApiKeyRequest<'a> {
    pub api_key: &'a PlatformApiKey,
    pub secret_hash: &'a str,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateSecretRequest<'a> {
    pub secret: &'a Secret,
    pub ciphertext: &'a str,
    pub key_version: &'a str,
    pub created_by: Option<&'a crank_core::UserId>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct RotateSecretRequest<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub secret_id: &'a SecretId,
    pub ciphertext: &'a str,
    pub key_version: &'a str,
    pub created_at: &'a OffsetDateTime,
    pub updated_at: &'a OffsetDateTime,
    pub created_by: Option<&'a crank_core::UserId>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateStreamSessionRequest<'a> {
    pub session: &'a StreamSession,
}

#[derive(Clone, Debug, PartialEq)]
pub struct UpdateStreamSessionStateRequest<'a> {
    pub session_id: &'a StreamSessionId,
    pub current_status: StreamStatus,
    pub next_status: StreamStatus,
    pub cursor: Option<&'a Value>,
    pub state: &'a Value,
    pub expires_at: Option<&'a OffsetDateTime>,
    pub last_poll_at: Option<&'a OffsetDateTime>,
    pub closed_at: Option<&'a OffsetDateTime>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StreamSessionFilter<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub agent_id: Option<&'a AgentId>,
    pub operation_id: Option<&'a OperationId>,
    pub status: Option<StreamStatus>,
    pub mode: Option<ExecutionMode>,
    pub limit: u32,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateAsyncJobRequest<'a> {
    pub job: &'a AsyncJobHandle,
}

#[derive(Clone, Debug, PartialEq)]
pub struct UpdateAsyncJobStatusRequest<'a> {
    pub job_id: &'a AsyncJobId,
    pub current_status: JobStatus,
    pub next_status: JobStatus,
    pub progress: &'a Value,
    pub result: Option<&'a Value>,
    pub error: Option<&'a Value>,
    pub expires_at: Option<&'a OffsetDateTime>,
    pub updated_at: &'a OffsetDateTime,
    pub finished_at: Option<&'a OffsetDateTime>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AsyncJobFilter<'a> {
    pub workspace_id: &'a WorkspaceId,
    pub agent_id: Option<&'a AgentId>,
    pub operation_id: Option<&'a OperationId>,
    pub status: Option<JobStatus>,
    pub limit: u32,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SaveSampleMetadataRequest<'a> {
    pub sample: &'a OperationSampleMetadata,
}

#[derive(Clone, Debug, PartialEq)]
pub struct SaveDescriptorMetadataRequest<'a> {
    pub descriptor: &'a DescriptorMetadata,
}
