use syn::{Attribute, Ident, Type};

/// Parsed column attribute options from `#[column(...)]`.
#[derive(Debug, Default)]
pub struct ColumnOpts {
    pub primary_key: bool,
    pub auto_increment: bool,
    pub sql_cast: Option<String>,
    pub type_cast: Option<String>,
    pub column_name: Option<String>,
    /// When true, the column has a database-level default. SKIP on create omits it (DB default used).
    pub has_default: bool,
}

impl ColumnOpts {
    pub fn from_attrs(attrs: &[Attribute]) -> Option<Self> {
        let attr = attrs.iter().find(|a| a.path().is_ident("column"))?;

        let mut opts = ColumnOpts::default();

        if attr.meta.require_path_only().is_ok() {
            return Some(opts);
        }

        let _ = attr.parse_nested_meta(|meta| {
            if meta.path.is_ident("primary_key") {
                opts.primary_key = true;
            } else if meta.path.is_ident("auto_increment") {
                opts.auto_increment = true;
            } else if meta.path.is_ident("sql_cast") {
                let value = meta.value()?;
                let lit: syn::LitStr = value.parse()?;
                opts.sql_cast = Some(lit.value());
            } else if meta.path.is_ident("type_cast") {
                let value = meta.value()?;
                let lit: syn::LitStr = value.parse()?;
                opts.type_cast = Some(lit.value());
            } else if meta.path.is_ident("name") {
                let value = meta.value()?;
                let lit: syn::LitStr = value.parse()?;
                opts.column_name = Some(lit.value());
            } else if meta.path.is_ident("has_default") {
                opts.has_default = true;
            }
            Ok(())
        });

        Some(opts)
    }
}

/// Parsed relation attribute options from `#[has_many(...)]`, `#[belongs_to(...)]`, `#[has_one(...)]`.
#[derive(Debug)]
pub struct RelationOpts {
    pub relation_type: String,
    /// Key pairs: (source_column, target_column). Single or composite.
    pub key_pairs: Vec<(String, String)>,
    /// Explicit table name. When None, inferred from field type via Model::TABLE_NAME.
    pub target_table: Option<String>,
    pub order: Option<String>,
    pub limit_per_parent: Option<usize>,
    pub hard_limit: Option<usize>,
}

impl RelationOpts {
    pub fn from_attrs(attrs: &[Attribute]) -> Option<Self> {
        for attr in attrs {
            let rel_type = if attr.path().is_ident("has_many") {
                "has_many"
            } else if attr.path().is_ident("belongs_to") {
                "belongs_to"
            } else if attr.path().is_ident("has_one") {
                "has_one"
            } else {
                continue;
            };

            let mut source = None;
            let mut target = None;
            let mut keys = None;
            let mut table = None;
            let mut order = None;
            let mut limit_per_parent = None;
            let mut hard_limit = None;

            let _ = attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("source") {
                    let value = meta.value()?;
                    let lit: syn::LitStr = value.parse()?;
                    source = Some(lit.value());
                } else if meta.path.is_ident("target") {
                    let value = meta.value()?;
                    let lit: syn::LitStr = value.parse()?;
                    target = Some(lit.value());
                } else if meta.path.is_ident("keys") {
                    let value = meta.value()?;
                    let lit: syn::LitStr = value.parse()?;
                    let s = lit.value();
                    let pairs: Vec<(String, String)> = s
                        .split(',')
                        .map(|p| p.trim())
                        .filter(|p| !p.is_empty())
                        .filter_map(|p| {
                            let parts: Vec<&str> = p.splitn(2, ':').collect();
                            if parts.len() == 2 {
                                Some((parts[0].trim().to_string(), parts[1].trim().to_string()))
                            } else {
                                None
                            }
                        })
                        .collect();
                    if !pairs.is_empty() {
                        keys = Some(pairs);
                    }
                } else if meta.path.is_ident("table") {
                    let value = meta.value()?;
                    let lit: syn::LitStr = value.parse()?;
                    table = Some(lit.value());
                } else if meta.path.is_ident("order") {
                    let value = meta.value()?;
                    let lit: syn::LitStr = value.parse()?;
                    order = Some(lit.value());
                } else if meta.path.is_ident("limit_per_parent") {
                    let value = meta.value()?;
                    let lit: syn::LitInt = value.parse()?;
                    limit_per_parent = lit.base10_parse().ok();
                } else if meta.path.is_ident("hard_limit") {
                    let value = meta.value()?;
                    let lit: syn::LitInt = value.parse()?;
                    hard_limit = lit.base10_parse().ok();
                }
                Ok(())
            });

            let key_pairs = if let Some(k) = keys {
                k
            } else if let (Some(s), Some(t)) = (source, target) {
                vec![(s, t)]
            } else {
                continue;
            };

            return Some(RelationOpts {
                relation_type: rel_type.to_string(),
                key_pairs,
                target_table: table,
                order,
                limit_per_parent,
                hard_limit,
            });
        }
        None
    }
}

/// Information about a single model field.
#[derive(Debug)]
pub struct FieldInfo {
    pub field_name: Ident,
    pub field_type: Type,
    pub column_name: String,
    pub opts: ColumnOpts,
    pub is_column: bool,
    pub relation_opts: Option<RelationOpts>,
}
