-- lua/prompt-tower/config.lua
-- Configuration management for prompt-tower.nvim

local M = {}

-- Default configuration
local defaults = {
  -- File discovery settings
  file_discovery = {
    max_depth = 10,
    include_hidden = true, -- Include hidden files (starting with .)
  },
  ignore_patterns = {
    '^%.git$',
    'node_modules',
    '.DS_Store',
    '*.pyc',
    '__pycache__',
    '.pytest_cache',
    '.venv',
    'venv',
    'target', -- Rust/Java
    'build',
    'dist',
    '.next',
    '.nuxt',
  },
  use_gitignore = true, -- Respect .gitignore files for ignore patterns
  use_towerignore = true, -- Respect .towerignore files for custom ignore patterns
  max_file_size_kb = 500, -- 500KB limit

  -- Context generation settings
  output_format = {
    -- Individual file block template
    block_template = '<file name="{fileNameWithExtension}" path="{rawFilePath}">\n{fileContent}\n</file>',

    -- Separator between file blocks
    block_separator = '\n',

    -- Whether to trim leading/trailing blank lines from file content
    block_trim_lines = true,

    -- Overall wrapper template (can be set to null to disable wrapping)
    wrapper_format = {
      template = '<context>\n{githubIssues}{treeBlock}<project_files>\n{blocks}\n</project_files>\n</context>',
    },

    -- Project tree configuration
    project_tree_format = {
      enabled = true,
      type = 'fullFilesAndDirectories', -- 'fullFilesAndDirectories', 'fullDirectoriesOnly', 'selectedFilesOnly'
      show_file_size = false,
      template = '<project_tree>\n{projectTree}\n</project_tree>\n',
    },

    -- Default template format preset (for backward compatibility)
    default_format = 'xml', -- Options: 'xml', 'markdown', 'minimal'

    -- Legacy template presets (for backward compatibility)
    presets = {
      xml = {
        block_template = '<file name="{fileNameWithExtension}" path="{rawFilePath}">\n{fileContent}\n</file>',
        separator = '\n\n',
        wrapper_template = '<!-- Generated by prompt-tower.nvim -->\n'
          .. '<!-- {fileCount} files selected -->\n\n{treeBlock}<project_files>\n{fileBlocks}\n</project_files>',
      },
      markdown = {
        block_template = '## {fileName}\n\n**Path:** `{rawFilePath}`\n\n```{fileExtension}\n{fileContent}\n```',
        separator = '\n\n---\n\n',
        wrapper_template = '# Project Context\n\n{treeBlock}## Selected Files\n\n{fileBlocks}',
      },
      minimal = {
        block_template = '// File: {rawFilePath}\n{fileContent}',
        separator = '\n\n',
        wrapper_template = '{fileBlocks}',
      },
    },
  },

  -- Project tree generation settings
  project_tree = {
    enabled = true,
    type = 'fullFilesAndDirectories', -- Options: 'fullFilesAndDirectories', 'fullDirectoriesOnly', 'selectedFilesOnly', 'none'
    show_file_size = false,
    template = '<project_tree>\n{projectTree}\n</project_tree>\n\n',
  },

  -- Keymaps
  keymaps = {
    toggle_selection = '<Space>',
    select_all = 'A',
    clear_all = 'C',
    generate_context = '<CR>',
    quit = 'q',
    help = '?',
  },

  -- Clipboard settings
  clipboard = {
    register = '+', -- System clipboard
    notify_on_copy = true,
  },
}

-- Current configuration (starts as copy of defaults)
local current_config = vim.deepcopy(defaults)

-- Initialization state
local initialized = false

--- Setup configuration with user options
--- @param opts table? User configuration options
function M.setup(opts)
  vim.validate('opts', opts or {}, 'table')

  -- Deep merge user options with defaults
  current_config = vim.tbl_deep_extend('force', current_config, opts or {})

  -- Validate configuration
  M.validate()

  -- Mark as initialized
  initialized = true
end

--- Validate current configuration
--- @return boolean success True if configuration is valid
function M.validate()
  local config = current_config

  -- Validate ignore patterns
  if config.ignore_patterns then
    vim.validate('ignore_patterns', config.ignore_patterns, 'table')
    for i, pattern in ipairs(config.ignore_patterns) do
      vim.validate(string.format('ignore_patterns[%d]', i), pattern, 'string')
    end
  end

  -- Validate max file size
  if config.max_file_size_kb then
    vim.validate('max_file_size_kb', config.max_file_size_kb, 'number')
    if config.max_file_size_kb <= 0 then
      error('max_file_size_kb must be positive')
    end
  end

  -- Validate output format templates
  if config.output_format then
    vim.validate('output_format.default_format', config.output_format.default_format, 'string')

    -- Validate default format is one of the allowed options
    local valid_formats = { 'xml', 'markdown', 'minimal' }
    local found = false
    for _, format in ipairs(valid_formats) do
      if config.output_format.default_format == format then
        found = true
        break
      end
    end
    if not found then
      error('output_format.default_format must be one of: ' .. table.concat(valid_formats, ', '))
    end

    -- Validate presets structure
    if config.output_format.presets then
      vim.validate('output_format.presets', config.output_format.presets, 'table')
      for preset_name, preset in pairs(config.output_format.presets) do
        vim.validate(
          string.format('output_format.presets.%s.block_template', preset_name),
          preset.block_template,
          'string'
        )
        vim.validate(string.format('output_format.presets.%s.separator', preset_name), preset.separator, 'string')
        vim.validate(
          string.format('output_format.presets.%s.wrapper_template', preset_name),
          preset.wrapper_template,
          'string'
        )
      end
    end
  end

  -- Validate project tree settings
  if config.project_tree then
    vim.validate('project_tree', config.project_tree, 'table')

    if config.project_tree.enabled ~= nil then
      vim.validate('project_tree.enabled', config.project_tree.enabled, 'boolean')
    end

    if config.project_tree.type then
      vim.validate('project_tree.type', config.project_tree.type, 'string')
      local valid_tree_types = { 'fullFilesAndDirectories', 'fullDirectoriesOnly', 'selectedFilesOnly', 'none' }
      local found_type = false
      for _, tree_type in ipairs(valid_tree_types) do
        if config.project_tree.type == tree_type then
          found_type = true
          break
        end
      end
      if not found_type then
        error('project_tree.type must be one of: ' .. table.concat(valid_tree_types, ', '))
      end
    end

    if config.project_tree.show_file_size ~= nil then
      vim.validate('project_tree.show_file_size', config.project_tree.show_file_size, 'boolean')
    end

    if config.project_tree.template then
      vim.validate('project_tree.template', config.project_tree.template, 'string')
    end
  end

  -- Validate UI settings
  if config.ui then
    if config.ui.width then
      vim.validate('ui.width', config.ui.width, 'number')
      if config.ui.width <= 0 or config.ui.width > 1 then
        error('ui.width must be between 0 and 1')
      end
    end
    if config.ui.height then
      vim.validate('ui.height', config.ui.height, 'number')
      if config.ui.height <= 0 or config.ui.height > 1 then
        error('ui.height must be between 0 and 1')
      end
    end
  end

  -- Validate clipboard register
  if config.clipboard and config.clipboard.register then
    vim.validate('clipboard.register', config.clipboard.register, 'string')
    if not vim.regex([[\v^[+*"a-zA-Z0-9]$]]):match_str(config.clipboard.register) then
      error('clipboard.register must be a valid Vim register')
    end
  end

  return true
end

--- Get current configuration
--- @return table Current configuration
function M.get()
  return current_config
end

--- Get the current template format being used
--- @return string Current template format name
function M.get_current_format()
  return current_config.output_format.current_format or current_config.output_format.default_format
end

--- Set the current template format (hot-swap)
--- @param format string Template format name ('xml', 'markdown', 'minimal')
function M.set_current_format(format)
  vim.validate('format', format, 'string')

  -- Validate format exists in presets
  if not current_config.output_format.presets[format] then
    error(
      'Unknown template format: '
        .. format
        .. '. Available formats: '
        .. table.concat(vim.tbl_keys(current_config.output_format.presets), ', ')
    )
  end

  -- Set the current format (not persisted, just for this session)
  current_config.output_format.current_format = format
end

--- Get available template formats
--- @return table List of available format names
function M.get_available_formats()
  return vim.tbl_keys(current_config.output_format.presets)
end

--- Get template configuration for current or specified format
--- @param format string? Template format name, defaults to current format
--- @return table Template configuration
function M.get_template_config(format)
  format = format or M.get_current_format()
  return current_config.output_format.presets[format]
end

--- Get a specific configuration value
--- @param key string Configuration key (supports dot notation)
--- @return any Configuration value
function M.get_value(key)
  vim.validate('key', key, 'string')

  local keys = vim.split(key, '.', { plain = true })
  local value = current_config

  for _, k in ipairs(keys) do
    if type(value) ~= 'table' or value[k] == nil then
      return nil
    end
    value = value[k]
  end

  return value
end

--- Set a specific configuration value
--- @param key string Configuration key (supports dot notation)
--- @param value any Configuration value
function M.set_value(key, value)
  vim.validate('key', key, 'string')

  local keys = vim.split(key, '.', { plain = true })
  local target = current_config

  -- Navigate to parent of target key
  for i = 1, #keys - 1 do
    local k = keys[i]
    if type(target[k]) ~= 'table' then
      target[k] = {}
    end
    target = target[k]
  end

  -- Set the value
  target[keys[#keys]] = value

  -- Re-validate configuration
  M.validate()
end

--- Get default configuration
--- @return table Default configuration
function M.get_defaults()
  return vim.deepcopy(defaults)
end

--- Reset configuration to defaults
function M.reset()
  current_config = vim.deepcopy(defaults)
  initialized = false
end

--- Export configuration for debugging
--- @return string Configuration as string
function M.export()
  return vim.inspect(current_config)
end

--- Check if plugin has been initialized
--- @return boolean True if plugin has been set up
function M.is_initialized()
  return initialized
end

return M
