use crate::error::DbError;
use crate::executor::context::ExecutionContext;
use crate::types::Value;

type SystemVarHandler = fn(&ExecutionContext) -> Value;

struct SystemVariable {
    name: &'static str,
    handler: SystemVarHandler,
}

static SYSTEM_VARIABLES: &[SystemVariable] = &[
    SystemVariable {
        name: "@@FETCH_STATUS",
        handler: |ctx| Value::Int(*ctx.session.fetch_status),
    },
    SystemVariable {
        name: "@@ERROR",
        handler: |ctx| {
            let code = ctx
                .frame
                .last_error
                .as_ref()
                .map(|e| e.number())
                .unwrap_or(0);
            Value::Int(code)
        },
    },
    SystemVariable {
        name: "@@LANGUAGE",
        handler: |ctx| Value::NVarChar(ctx.options.language.clone()),
    },
    SystemVariable {
        name: "@@TEXTSIZE",
        handler: |ctx| Value::Int(ctx.options.textsize),
    },
    SystemVariable {
        name: "@@MAX_PRECISION",
        handler: |_| Value::TinyInt(38),
    },
    SystemVariable {
        name: "@@DATEFIRST",
        handler: |ctx| Value::TinyInt(ctx.options.datefirst as u8),
    },
    SystemVariable {
        name: "@@TRANCOUNT",
        handler: |ctx| Value::Int(ctx.frame.trancount as i32),
    },
    SystemVariable {
        name: "@@IDENTITY",
        handler: |ctx| match ctx.current_scope_identity().or(*ctx.session.last_identity) {
            Some(id) => Value::BigInt(id),
            None => Value::Null,
        },
    },
    SystemVariable {
        name: "@@PROCID",
        handler: |ctx| match ctx.current_procid() {
            Some(id) => Value::Int(id),
            None => Value::Null,
        },
    },
    SystemVariable {
        name: "@@SERVERNAME",
        handler: |_| Value::NVarChar("localhost".into()),
    },
    SystemVariable {
        name: "@@SERVICENAME",
        handler: |_| Value::NVarChar("MSSQLSERVER".into()),
    },
    SystemVariable {
        name: "@@SPID",
        handler: |ctx| Value::SmallInt(ctx.session_id() as i16),
    },
    SystemVariable {
        name: "@@VERSION",
        handler: |_| {
            Value::NVarChar(
                "Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (iridium_sql emulator)".into(),
            )
        },
    },
    SystemVariable {
        name: "@@MICROSOFTVERSION",
        handler: |_| Value::Int(0x10001009),
    },
];

pub(crate) fn resolve_system_variable(
    name: &str,
    ctx: &ExecutionContext,
) -> Result<Option<Value>, DbError> {
    for var in SYSTEM_VARIABLES {
        if var.name.eq_ignore_ascii_case(name) {
            return Ok(Some((var.handler)(ctx)));
        }
    }
    Ok(None)
}
