use crate::errors::{CopulaError, FitError};

pub fn theta_from_tau(tau: f64) -> Result<f64, CopulaError> {
    if !tau.is_finite() || tau <= 0.0 || tau >= 1.0 {
        return Err(FitError::Failed {
            reason: "clayton pair fit requires tau in (0, 1)",
        }
        .into());
    }
    Ok(2.0 * tau / (1.0 - tau))
}

pub fn log_pdf(u1: f64, u2: f64, theta: f64) -> Result<f64, CopulaError> {
    if !theta.is_finite() || theta <= 0.0 {
        return Err(FitError::Failed {
            reason: "clayton pair theta must be positive",
        }
        .into());
    }
    let sum_power = u1.powf(-theta) + u2.powf(-theta) - 1.0;
    Ok((1.0 + theta).ln()
        - (1.0 + theta) * (u1.ln() + u2.ln())
        - (2.0 + 1.0 / theta) * sum_power.ln())
}

pub fn cond_first_given_second(u1: f64, u2: f64, theta: f64) -> Result<f64, CopulaError> {
    let s = u1.powf(-theta) + u2.powf(-theta) - 1.0;
    Ok(s.powf(-1.0 / theta - 1.0) * u2.powf(-theta - 1.0))
}

pub fn cond_second_given_first(u1: f64, u2: f64, theta: f64) -> Result<f64, CopulaError> {
    let s = u1.powf(-theta) + u2.powf(-theta) - 1.0;
    Ok(s.powf(-1.0 / theta - 1.0) * u1.powf(-theta - 1.0))
}

pub fn inv_first_given_second(p: f64, u2: f64, theta: f64) -> Result<f64, CopulaError> {
    Ok((1.0 + u2.powf(-theta) * (p.powf(-theta / (1.0 + theta)) - 1.0)).powf(-1.0 / theta))
}

pub fn inv_second_given_first(u1: f64, p: f64, theta: f64) -> Result<f64, CopulaError> {
    Ok((1.0 + u1.powf(-theta) * (p.powf(-theta / (1.0 + theta)) - 1.0)).powf(-1.0 / theta))
}
