-- smartparens.lua
-- ============================================================================
-- Automatic pairing of brackets, quotes, and other delimiters.
-- Similar to Emacs smartparens or electric-pair-mode.
-- ============================================================================

local smartparens = {}

-- Configuration
smartparens.config = {
    enabled = true,
    pairs = {
        ["("] = ")",
        ["["] = "]",
        ["{"] = "}",
        ['"'] = '"',
        ["'"] = "'",
        ["`"] = "`",
    },
    -- Skip closing when next char is the same closer
    skip_when_next = true,
    -- Delete pair together when backspacing empty pair
    delete_pair = true,
    -- Wrap region with pair when mark is active
    wrap_region = true,
}

-- State
smartparens.active = true

-- Check if char is an opener
function smartparens.is_opener(char)
    return smartparens.config.pairs[char] ~= nil
end

-- Check if char is a closer
function smartparens.is_closer(char)
    for opener, closer in pairs(smartparens.config.pairs) do
        if closer == char and opener ~= closer then
            return true, opener
        end
    end
    return false, nil
end

-- Get closer for an opener
function smartparens.get_closer(opener)
    return smartparens.config.pairs[opener]
end

-- Check if we're inside a string (simple heuristic)
function smartparens.in_string()
    local buf = editor.buffer
    local cursor = editor.cursor
    local line = buf:line(cursor.line)
    local quote_count = 0
    local dquote_count = 0

    for i = 1, cursor.column do
        local c = line:sub(i, i)
        if c == "'" then quote_count = quote_count + 1 end
        if c == '"' then dquote_count = dquote_count + 1 end
    end

    return (quote_count % 2 == 1) or (dquote_count % 2 == 1)
end

-- Get char at cursor
function smartparens.char_at_cursor()
    local buf = editor.buffer
    local cursor = editor.cursor
    local line = buf:line(cursor.line)
    if cursor.column < #line then
        return line:sub(cursor.column + 1, cursor.column + 1)
    end
    return nil
end

-- Get char before cursor
function smartparens.char_before_cursor()
    local buf = editor.buffer
    local cursor = editor.cursor
    local line = buf:line(cursor.line)
    if cursor.column > 0 then
        return line:sub(cursor.column, cursor.column)
    end
    return nil
end

-- Insert pair and position cursor between them
function smartparens.insert_pair(opener)
    if not smartparens.active or not smartparens.config.enabled then
        return false
    end

    local closer = smartparens.get_closer(opener)
    if not closer then
        return false
    end

    local buf = editor.buffer
    local cursor = editor.cursor

    -- Check if we have a region selected (for wrapping)
    if smartparens.config.wrap_region and buf:has_mark() then
        local region = buf:get_region(cursor)
        if region then
            local text = buf:get_text_in_range(region)
            buf:erase(region)
            buf:insert(region.start, opener .. text .. closer)
            -- Position cursor after the wrapped text
            editor.cursor = lumacs.Position(region.start.line, region.start.column + #text + 1)
            buf:deactivate_mark()
            return true
        end
    end

    -- For symmetric pairs (quotes), check if we should skip
    if opener == closer then
        local next_char = smartparens.char_at_cursor()
        if next_char == opener then
            -- Skip instead of inserting
            editor.cursor = lumacs.Position(cursor.line, cursor.column + 1)
            return true
        end
    end

    -- Insert the pair
    buf:insert(cursor, opener .. closer)
    -- Position cursor between the pair
    editor.cursor = lumacs.Position(cursor.line, cursor.column + 1)
    return true
end

-- Handle closing delimiter - skip if already there
function smartparens.handle_closer(closer)
    if not smartparens.active or not smartparens.config.skip_when_next then
        return false
    end

    local next_char = smartparens.char_at_cursor()
    if next_char == closer then
        local cursor = editor.cursor
        editor.cursor = lumacs.Position(cursor.line, cursor.column + 1)
        return true
    end

    return false
end

-- Handle backspace - delete pair together if empty
function smartparens.handle_backspace()
    if not smartparens.active or not smartparens.config.delete_pair then
        return false
    end

    local prev = smartparens.char_before_cursor()
    local next = smartparens.char_at_cursor()

    if prev and next then
        local expected_closer = smartparens.get_closer(prev)
        if expected_closer == next then
            -- Delete both chars
            local buf = editor.buffer
            local cursor = editor.cursor
            local range = lumacs.Range(
                lumacs.Position(cursor.line, cursor.column - 1),
                lumacs.Position(cursor.line, cursor.column + 1)
            )
            buf:erase(range)
            editor.cursor = lumacs.Position(cursor.line, cursor.column - 1)
            return true
        end
    end

    return false
end

-- Toggle smartparens
function smartparens.toggle()
    smartparens.active = not smartparens.active
    if smartparens.active then
        editor:message("Smartparens enabled")
    else
        editor:message("Smartparens disabled")
    end
end

-- Register commands
editor:register_command("smartparens-mode", "Toggle smartparens auto-pairing", function(args)
    smartparens.toggle()
    return {success = true}
end, {"sp-mode"})

editor:register_command("smartparens-strict-mode", "Toggle strict pairing rules", function(args)
    smartparens.config.skip_when_next = not smartparens.config.skip_when_next
    smartparens.config.delete_pair = not smartparens.config.delete_pair
    local state = smartparens.config.skip_when_next and "enabled" or "disabled"
    editor:message("Strict mode " .. state)
    return {success = true}
end)

-- Define minor mode
lumacs.define_minor_mode("smartparens-mode", {
    lighter = "()",
    global = false,
    setup = function()
        smartparens.active = true
        editor:message("Smartparens enabled")
    end,
    cleanup = function()
        smartparens.active = false
        editor:message("Smartparens disabled")
    end
})

-- Store in lumacs namespace
lumacs.smartparens = smartparens

return smartparens
