use rusqlite::Row;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Project {
    /// workspace.id（也是旧 projects.id，向后兼容所有关联表）
    pub id: String,
    /// profile.id，用于跨空间共享更新
    pub profile_id: Option<String>,
    /// 等于 id，前端可用于区分语义
    pub workspace_id: Option<String>,
    /// 关联的 git_repos.id（仓库锚点）
    pub repo_id: Option<String>,
    pub name: String,
    pub description: Option<String>,
    #[serde(rename = "type")]
    pub type_: Option<String>,
    // ── profile 级（项目档案，跨空间共享）────────────────────────
    pub status: Option<String>,
    pub priority: Option<String>,
    pub tech_stack: Option<String>,
    pub tags: Option<String>,
    pub repo_url: Option<String>,
    pub upstream_url: Option<String>,
    pub default_branch: Option<String>,
    pub staging_url: Option<String>,
    pub prod_url: Option<String>,
    pub server_note: Option<String>,
    // ── workspace 级（每空间独立）───────────────────────────────
    pub space_id: Option<String>,
    pub wsl_path: Option<String>,
    pub win_path: Option<String>,
    pub platform: Option<String>,
    pub recent_focus: Option<String>,
    pub startup_commands: Option<String>,
    pub last_push_at: Option<String>,
    pub updated_at: Option<String>,
}

impl Project {
    /// 从 project_workspaces JOIN project_profiles 的查询结果构造
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            profile_id: row.get("profile_id").ok(),
            workspace_id: row.get("workspace_id").ok(),
            repo_id: row.get("repo_id").ok(),
            name: row.get("name")?,
            description: row.get("description").ok(),
            type_: row.get("type").ok(),
            status: row.get("status").ok(),
            priority: row.get("priority").ok(),
            tech_stack: row.get("tech_stack").ok(),
            tags: row.get("tags").ok(),
            repo_url: row.get("repo_url").ok(),
            upstream_url: row.get("upstream_url").ok(),
            default_branch: row.get("default_branch").ok(),
            staging_url: row.get("staging_url").ok(),
            prod_url: row.get("prod_url").ok(),
            server_note: row.get("server_note").ok(),
            space_id: row.get("space_id").ok(),
            wsl_path: row.get("wsl_path").ok(),
            win_path: row.get("win_path").ok(),
            platform: row.get("platform").ok(),
            recent_focus: row.get("recent_focus").ok(),
            startup_commands: row.get("startup_commands").ok(),
            last_push_at: row.get("last_push_at").ok(),
            updated_at: row.get("updated_at").ok(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ActivityLog {
    pub id: i64,
    pub project_id: String,
    pub summary: String,
    pub created_at: Option<String>,
}

impl ActivityLog {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            project_id: row.get("project_id")?,
            summary: row.get("summary")?,
            created_at: row.get("created_at")?,
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProductGroup {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub space_id: Option<String>,
    pub sort_order: i32,
}

impl ProductGroup {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            name: row.get("name")?,
            description: row.get("description")?,
            space_id: row.get("space_id").ok(),
            sort_order: row.get("sort_order").unwrap_or(0),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectGroupMap {
    pub project_id: String,
    pub group_id: String,
    pub role: Option<String>,
}

impl ProjectGroupMap {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            project_id: row.get("project_id")?,
            group_id: row.get("group_id")?,
            role: row.get("role")?,
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Space {
    pub id: String,
    pub name: String,
    pub is_current: bool,
    pub created_at: Option<String>,
}

impl Space {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            name: row.get("name")?,
            is_current: row.get::<_, i64>("is_current")? != 0,
            created_at: row.get("created_at")?,
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DeployCommand {
    pub id: String,
    pub project_id: String,
    pub name: String,
    pub command: String,
    pub run_in: String,
}

impl DeployCommand {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            project_id: row.get("project_id")?,
            name: row.get("name")?,
            command: row.get("command")?,
            run_in: row.get("run_in")?,
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CicdConfig {
    pub id: String,
    pub project_id: String,
    pub config_type: String,
    pub trigger_branch: String,
    pub build_command: Option<String>,
    pub node_version: Option<String>,
    pub server_host: Option<String>,
    pub server_user: Option<String>,
    pub deploy_path: Option<String>,
    pub start_command: Option<String>,
    pub oss_endpoint: Option<String>,
    pub created_at: Option<String>,
}

impl CicdConfig {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            project_id: row.get("project_id")?,
            config_type: row.get("config_type")?,
            trigger_branch: row.get("trigger_branch")?,
            build_command: row.get("build_command").ok(),
            node_version: row.get("node_version").ok(),
            server_host: row.get("server_host").ok(),
            server_user: row.get("server_user").ok(),
            deploy_path: row.get("deploy_path").ok(),
            start_command: row.get("start_command").ok(),
            oss_endpoint: row.get("oss_endpoint").ok(),
            created_at: row.get("created_at").ok(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowResult {
    pub content: String,
    pub path: String,
    pub secrets: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GitRepo {
    pub id: String,
    pub repo_url: String,
    pub name: String,
    pub description: Option<String>,
    pub default_branch: Option<String>,
    pub upstream_url: Option<String>,
    pub staging_url: Option<String>,
    pub prod_url: Option<String>,
    pub server_note: Option<String>,
    pub status: Option<String>,
    pub priority: Option<String>,
    pub tech_stack: Option<String>,
    pub last_synced_at: Option<String>,
    pub created_at: Option<String>,
}

impl GitRepo {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            repo_url: row.get("repo_url")?,
            name: row.get("name")?,
            description: row.get("description").ok(),
            default_branch: row.get("default_branch").ok(),
            upstream_url: row.get("upstream_url").ok(),
            staging_url: row.get("staging_url").ok(),
            prod_url: row.get("prod_url").ok(),
            server_note: row.get("server_note").ok(),
            status: row.get("status").ok(),
            priority: row.get("priority").ok(),
            tech_stack: row.get("tech_stack").ok(),
            last_synced_at: row.get("last_synced_at").ok(),
            created_at: row.get("created_at").ok(),
        })
    }
}

// ── 源码库 ─────────────────────────────────────────────────────────────────

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceCategory {
    pub id: String,
    pub name: String,
    pub icon: String,
    pub color: String,
    pub sort_order: i32,
}

impl SourceCategory {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            name: row.get("name")?,
            icon: row.get::<_, String>("icon").unwrap_or_else(|_| "📁".into()),
            color: row.get::<_, String>("color").unwrap_or_else(|_| "#6B7280".into()),
            sort_order: row.get("sort_order").unwrap_or(0),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceProject {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub category_id: Option<String>,
    pub language: Option<String>,
    pub tags: Option<String>,
    pub repo_url: Option<String>,
    pub introduction: Option<String>,
    pub readme: Option<String>,
    pub notes: Option<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
    // join 字段
    pub category_name: Option<String>,
    pub version_count: Option<i32>,
}

impl SourceProject {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            name: row.get("name")?,
            description: row.get("description").ok(),
            category_id: row.get("category_id").ok(),
            language: row.get("language").ok(),
            tags: row.get("tags").ok(),
            repo_url: row.get("repo_url").ok(),
            introduction: row.get("introduction").ok(),
            readme: row.get("readme").ok(),
            notes: row.get("notes").ok(),
            created_at: row.get("created_at").ok(),
            updated_at: row.get("updated_at").ok(),
            category_name: row.get("category_name").ok(),
            version_count: row.get("version_count").ok(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceVersion {
    pub id: String,
    pub project_id: String,
    pub version: String,
    pub file_path: String,
    pub archive_path: Option<String>,
    pub file_size: i64,
    pub is_primary: bool,
    pub dir_tree: Option<String>,
    pub notes: Option<String>,
    pub imported_at: Option<String>,
}

impl SourceVersion {
    pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
        Ok(Self {
            id: row.get("id")?,
            project_id: row.get("project_id")?,
            version: row.get("version")?,
            file_path: row.get("file_path")?,
            archive_path: row.get("archive_path").ok(),
            file_size: row.get("file_size").unwrap_or(0),
            is_primary: row.get::<_, i64>("is_primary").unwrap_or(0) != 0,
            dir_tree: row.get("dir_tree").ok(),
            notes: row.get("notes").ok(),
            imported_at: row.get("imported_at").ok(),
        })
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DirEntry {
    pub name: String,
    #[serde(rename = "type")]
    pub type_: String,
    pub children_count: Option<i32>,
}
