//! Skill registered to a project.
//!
//! Skills can be sourced manually (by project admin) or auto-discovered from
//! repositories within the project (via `.claude/skills/` directory scanning).

use crate::{DateTimeUtc, ProjectId, RepoId, UserId};
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

/// Skill source: `"manual"` or `"repo"`.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum SkillSource {
    Manual,
    Repo,
}

impl std::fmt::Display for SkillSource {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SkillSource::Manual => write!(f, "manual"),
            SkillSource::Repo => write!(f, "repo"),
        }
    }
}

impl std::str::FromStr for SkillSource {
    type Err = &'static str;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(SkillSource::Manual),
            "repo" => Ok(SkillSource::Repo),
            _ => Err("unknown skill source"),
        }
    }
}

/// Parsed frontmatter from a skill's SKILL.md file.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SkillMetadata {
    /// Human-readable name (falls back to slug/folder name).
    #[serde(default)]
    pub name: Option<String>,
    /// Short description of what the skill does.
    #[serde(default)]
    pub description: Option<String>,
    /// SPDX license identifier.
    #[serde(default)]
    pub license: Option<String>,
    /// Compatibility notes (e.g. "Requires openspec CLI").
    #[serde(default)]
    pub compatibility: Option<String>,
    /// Free-form metadata from the frontmatter.
    #[serde(default)]
    pub metadata: serde_json::Value,
}

impl From<serde_json::Value> for SkillMetadata {
    fn from(v: serde_json::Value) -> Self {
        serde_json::from_value(v).unwrap_or_default()
    }
}

/// Skill record persisted in the `project_skill` table.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "project_skill")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i64,
    /// Project this skill belongs to.
    pub project_uuid: ProjectId,
    /// URL-safe identifier, unique within a project.
    pub slug: String,
    /// Display name (extracted from frontmatter or folder name).
    pub name: String,
    /// Optional short description.
    pub description: Option<String>,
    /// `"manual"` or `"repo"`.
    pub source: String,
    /// If source=repo, the repo this skill was discovered from.
    #[sea_orm(nullable)]
    pub repo_id: Option<RepoId>,
    /// If source=repo, the commit SHA where the skill was found.
    #[sea_orm(nullable)]
    pub commit_sha: Option<String>,
    /// If source=repo, the blob SHA of the SKILL.md file.
    #[sea_orm(nullable)]
    pub blob_hash: Option<String>,
    /// Raw markdown content (SKILL.md body after frontmatter).
    pub content: String,
    /// Full frontmatter as JSON.
    #[sea_orm(column_type = "JsonBinary")]
    pub metadata: serde_json::Value,
    /// Whether this skill is currently active.
    pub enabled: bool,
    /// Who added this skill (null for repo-sourced skills).
    #[sea_orm(nullable)]
    pub created_by: Option<UserId>,
    pub created_at: DateTimeUtc,
    pub updated_at: DateTimeUtc,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

impl Model {
    pub fn source_enum(&self) -> Result<SkillSource, &'static str> {
        self.source.parse()
    }

    pub fn metadata_parsed(&self) -> SkillMetadata {
        SkillMetadata::from(self.metadata.clone())
    }
}
