//! Create, update, delete project skills.

use crate::AppService;
use crate::error::AppError;
use super::info::SkillResponse;
use chrono::Utc;
use models::projects::project_skill::{Column as C, Entity as SkillEntity};
use models::ActiveModelTrait;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, Set};
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use uuid::Uuid;

#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct CreateSkillRequest {
    pub slug: String,
    pub name: Option<String>,
    pub description: Option<String>,
    pub content: String,
    pub metadata: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpdateSkillRequest {
    pub name: Option<String>,
    pub description: Option<String>,
    pub content: Option<String>,
    pub metadata: Option<serde_json::Value>,
    pub enabled: Option<bool>,
}

#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct DeleteSkillResponse {
    pub deleted: bool,
    pub slug: String,
}

fn validate_slug(slug: &str) -> Result<(), AppError> {
    if slug.is_empty() || slug.len() > 255 {
        return Err(AppError::BadRequest("Invalid slug".to_string()));
    }
    if !slug
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
    {
        return Err(AppError::BadRequest(
            "Slug must contain only ASCII letters, numbers, hyphens, and underscores".to_string(),
        ));
    }
    Ok(())
}

impl AppService {
    /// Add a skill manually to a project.
    pub async fn skill_create(
        &self,
        project_uuid: String,
        request: CreateSkillRequest,
        ctx: &Session,
    ) -> Result<SkillResponse, AppError> {
        validate_slug(&request.slug)?;

        let project_id = Uuid::parse_str(&project_uuid)
            .map_err(|_| AppError::BadRequest("Invalid project UUID".to_string()))?;

        let user_id = ctx
            .user()
            .ok_or_else(|| AppError::Unauthorized)?;

        // Check for duplicate slug within project
        let exists = SkillEntity::find()
            .filter(C::ProjectUuid.eq(project_id))
            .filter(C::Slug.eq(&request.slug))
            .one(&self.db)
            .await?;
        if exists.is_some() {
            return Err(AppError::Conflict(format!(
                "Skill '{}' already exists in this project",
                request.slug
            )));
        }

        let now = Utc::now();
        let metadata = request.metadata.unwrap_or(serde_json::Value::Object(Default::default()));
        let name = request.name.unwrap_or_else(|| request.slug.clone());

        let active = models::projects::project_skill::ActiveModel {
            id: Set(0), // auto-increment
            project_uuid: Set(project_id),
            slug: Set(request.slug),
            name: Set(name),
            description: Set(request.description),
            source: Set("manual".to_string()),
            repo_id: Set(None),
            commit_sha: Set(None),
            blob_hash: Set(None),
            content: Set(request.content),
            metadata: Set(metadata),
            enabled: Set(true),
            created_by: Set(Some(user_id)),
            created_at: Set(now),
            updated_at: Set(now),
        };

        let inserted = active.insert(&self.db).await?;
        Ok(SkillResponse::from(inserted))
    }

    /// Update an existing skill.
    pub async fn skill_update(
        &self,
        project_uuid: String,
        slug: String,
        request: UpdateSkillRequest,
        _ctx: &Session,
    ) -> Result<SkillResponse, AppError> {
        let project_id = Uuid::parse_str(&project_uuid)
            .map_err(|_| AppError::BadRequest("Invalid project UUID".to_string()))?;

        let skill = SkillEntity::find()
            .filter(C::ProjectUuid.eq(project_id))
            .filter(C::Slug.eq(&slug))
            .one(&self.db)
            .await?
            .ok_or_else(|| AppError::NotFound("Skill not found".to_string()))?;

        let mut active: models::projects::project_skill::ActiveModel = skill.into();
        if let Some(name) = request.name {
            active.name = Set(name);
        }
        if let Some(description) = request.description {
            active.description = Set(Some(description));
        }
        if let Some(content) = request.content {
            active.content = Set(content);
        }
        if let Some(metadata) = request.metadata {
            active.metadata = Set(metadata);
        }
        if let Some(enabled) = request.enabled {
            active.enabled = Set(enabled);
        }
        active.updated_at = Set(Utc::now());

        let updated = active.update(&self.db).await?;
        Ok(SkillResponse::from(updated))
    }

    /// Delete a skill from a project.
    pub async fn skill_delete(
        &self,
        project_uuid: String,
        slug: String,
        _ctx: &Session,
    ) -> Result<DeleteSkillResponse, AppError> {
        let project_id = Uuid::parse_str(&project_uuid)
            .map_err(|_| AppError::BadRequest("Invalid project UUID".to_string()))?;

        let skill = SkillEntity::find()
            .filter(C::ProjectUuid.eq(project_id))
            .filter(C::Slug.eq(&slug))
            .one(&self.db)
            .await?
            .ok_or_else(|| AppError::NotFound("Skill not found".to_string()))?;

        let deleted = SkillEntity::delete_by_id(skill.id).exec(&self.db).await?;

        Ok(DeleteSkillResponse {
            deleted: deleted.rows_affected > 0,
            slug,
        })
    }
}
