use codex_config::HooksFile;

/// Parsed plugin metadata parameterized by its resource locator representation.
///
/// Host loading uses absolute paths, while resolved packages replace them with
/// authority-bound locators before exposing the manifest to consumers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PluginManifest<Resource> {
    pub name: String,
    pub version: Option<String>,
    pub description: Option<String>,
    pub keywords: Vec<String>,
    pub paths: PluginManifestPaths<Resource>,
    pub interface: Option<PluginManifestInterface<Resource>>,
}

/// Component resources declared by a plugin manifest.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PluginManifestPaths<Resource> {
    pub skills: Option<Resource>,
    pub mcp_servers: Option<Resource>,
    pub apps: Option<Resource>,
    pub hooks: Option<PluginManifestHooks<Resource>>,
}

/// Hook declarations embedded in or referenced by a plugin manifest.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PluginManifestHooks<Resource> {
    Paths(Vec<Resource>),
    Inline(Vec<HooksFile>),
}

/// Optional model- and UI-facing plugin metadata.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PluginManifestInterface<Resource> {
    pub display_name: Option<String>,
    pub short_description: Option<String>,
    pub long_description: Option<String>,
    pub developer_name: Option<String>,
    pub category: Option<String>,
    pub capabilities: Vec<String>,
    pub website_url: Option<String>,
    pub privacy_policy_url: Option<String>,
    pub terms_of_service_url: Option<String>,
    pub default_prompt: Option<Vec<String>>,
    pub brand_color: Option<String>,
    pub composer_icon: Option<Resource>,
    pub logo: Option<Resource>,
    pub screenshots: Vec<Resource>,
}

impl<Resource> Default for PluginManifestInterface<Resource> {
    fn default() -> Self {
        Self {
            display_name: None,
            short_description: None,
            long_description: None,
            developer_name: None,
            category: None,
            capabilities: Vec::new(),
            website_url: None,
            privacy_policy_url: None,
            terms_of_service_url: None,
            default_prompt: None,
            brand_color: None,
            composer_icon: None,
            logo: None,
            screenshots: Vec::new(),
        }
    }
}

impl<Resource> PluginManifest<Resource> {
    pub(crate) fn try_map_resources<Mapped, Error>(
        self,
        mut map: impl FnMut(Resource) -> Result<Mapped, Error>,
    ) -> Result<PluginManifest<Mapped>, Error> {
        let PluginManifest {
            name,
            version,
            description,
            keywords,
            paths,
            interface,
        } = self;
        let PluginManifestPaths {
            skills,
            mcp_servers,
            apps,
            hooks,
        } = paths;
        let hooks = match hooks {
            Some(PluginManifestHooks::Paths(paths)) => Some(PluginManifestHooks::Paths(
                paths
                    .into_iter()
                    .map(&mut map)
                    .collect::<Result<Vec<_>, _>>()?,
            )),
            Some(PluginManifestHooks::Inline(hooks)) => Some(PluginManifestHooks::Inline(hooks)),
            None => None,
        };
        let interface = match interface {
            Some(interface) => {
                let PluginManifestInterface {
                    display_name,
                    short_description,
                    long_description,
                    developer_name,
                    category,
                    capabilities,
                    website_url,
                    privacy_policy_url,
                    terms_of_service_url,
                    default_prompt,
                    brand_color,
                    composer_icon,
                    logo,
                    screenshots,
                } = interface;
                Some(PluginManifestInterface {
                    display_name,
                    short_description,
                    long_description,
                    developer_name,
                    category,
                    capabilities,
                    website_url,
                    privacy_policy_url,
                    terms_of_service_url,
                    default_prompt,
                    brand_color,
                    composer_icon: composer_icon.map(&mut map).transpose()?,
                    logo: logo.map(&mut map).transpose()?,
                    screenshots: screenshots
                        .into_iter()
                        .map(&mut map)
                        .collect::<Result<Vec<_>, _>>()?,
                })
            }
            None => None,
        };

        Ok(PluginManifest {
            name,
            version,
            description,
            keywords,
            paths: PluginManifestPaths {
                skills: skills.map(&mut map).transpose()?,
                mcp_servers: mcp_servers.map(&mut map).transpose()?,
                apps: apps.map(&mut map).transpose()?,
                hooks,
            },
            interface,
        })
    }
}
