//! Regex functions: regexp_count, regexp_extract, regexp_like, etc.

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::types::array::Array;
use crate::types::matcher::ExactMatcher;
use crate::types::{Type, BOOLEAN, INT, STRING};

/// regexp_count(string, pattern) - Count regex matches.
#[derive(Default, Clone)]
pub struct RegexpCount;

impl FunctionDef for RegexpCount {
    fn name(&self) -> &'static str {
        "regexp_count"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(INT)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_extract_all(string, pattern) - Extract all regex matches.
#[derive(Default, Clone)]
pub struct RegexpExtractAll2;

impl FunctionDef for RegexpExtractAll2 {
    fn name(&self) -> &'static str {
        "regexp_extract_all"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(Array::new(STRING).into())
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_extract_all(string, pattern, group) - Extract all regex matches for a group.
#[derive(Default, Clone)]
pub struct RegexpExtractAll3;

impl FunctionDef for RegexpExtractAll3 {
    fn name(&self) -> &'static str {
        "regexp_extract_all"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("group", ExactMatcher::of(INT))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(Array::new(STRING).into())
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_extract(string, pattern) - Extract first regex match.
#[derive(Default, Clone)]
pub struct RegexpExtract2;

impl FunctionDef for RegexpExtract2 {
    fn name(&self) -> &'static str {
        "regexp_extract"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_extract(string, pattern, group) - Extract first regex match for a group.
#[derive(Default, Clone)]
pub struct RegexpExtract3;

impl FunctionDef for RegexpExtract3 {
    fn name(&self) -> &'static str {
        "regexp_extract"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("group", ExactMatcher::of(INT))
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // No explicit return type means use first parameter's type
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_like(string, pattern) - Check if string matches regex.
#[derive(Default, Clone)]
pub struct RegexpLike;

impl FunctionDef for RegexpLike {
    fn name(&self) -> &'static str {
        "regexp_like"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_position(string, pattern) - Find position of first regex match.
#[derive(Default, Clone)]
pub struct RegexpPosition2;

impl FunctionDef for RegexpPosition2 {
    fn name(&self) -> &'static str {
        "regexp_position"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(INT)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_position(string, pattern, start) - Find position of first regex match starting from position.
#[derive(Default, Clone)]
pub struct RegexpPosition3;

impl FunctionDef for RegexpPosition3 {
    fn name(&self) -> &'static str {
        "regexp_position"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("start", ExactMatcher::of(INT))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(INT)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_position(string, pattern, start, occurrence) - Find position of nth regex match.
#[derive(Default, Clone)]
pub struct RegexpPosition4;

impl FunctionDef for RegexpPosition4 {
    fn name(&self) -> &'static str {
        "regexp_position"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("start", ExactMatcher::of(INT))
            .with("occurrence", ExactMatcher::of(INT))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(INT)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_replace(string, pattern) - Remove all regex matches.
#[derive(Default, Clone)]
pub struct RegexpReplace2;

impl FunctionDef for RegexpReplace2 {
    fn name(&self) -> &'static str {
        "regexp_replace"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_replace(string, pattern, replacement) - Replace all regex matches.
#[derive(Default, Clone)]
pub struct RegexpReplace3;

impl FunctionDef for RegexpReplace3 {
    fn name(&self) -> &'static str {
        "regexp_replace"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
            .with("replacement", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(STRING)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

/// regexp_split(string, pattern) - Split string by regex.
#[derive(Default, Clone)]
pub struct RegexpSplit;

impl FunctionDef for RegexpSplit {
    fn name(&self) -> &'static str {
        "regexp_split"
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("string", ExactMatcher::of(STRING))
            .with("pattern", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(Array::new(STRING).into())
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}
