require("codecompanion").setup({
  adapters = {
    http = {
      anthropic = function()
        return require("codecompanion.adapters").extend("anthropic", {
          env = {
            api_key = "cmd:op read op://personal/Anthropic_API/credential --no-newline",
          },
          schema = {
            extended_thinking = {
              default = true,
            },
          },
        })
      end,
      deepseek = function()
        return require("codecompanion.adapters").extend("deepseek", {
          env = {
            api_key = "cmd:op read op://personal/DeepSeek_API/credential --no-newline",
          },
        })
      end,
      gemini = function()
        return require("codecompanion.adapters").extend("gemini", {
          env = {
            api_key = "cmd:op read op://personal/Gemini_API/credential --no-newline",
          },
        })
      end,
      mistral = function()
        return require("codecompanion.adapters").extend("mistral", {
          env = {
            api_key = "cmd:op read op://personal/Mistral_API/credential --no-newline",
          },
        })
      end,
      novita = function()
        return require("codecompanion.adapters").extend("novita", {
          env = {
            api_key = "cmd:op read op://personal/Novita_API/credential --no-newline",
          },
          schema = {
            model = {
              default = function()
                return "qwen/qwen3-coder-480b-a35b-instruct"
              end,
            },
          },
        })
      end,
      ollama = function()
        return require("codecompanion.adapters").extend("ollama", {
          schema = {
            model = {
              default = "qwen3:latest",
            },
            num_ctx = {
              default = 20000,
            },
          },
        })
      end,
      openai = function()
        return require("codecompanion.adapters").extend("openai", {
          opts = {
            stream = true,
          },
          env = {
            api_key = "cmd:op read op://personal/OpenAI_API/credential --no-newline",
          },
          schema = {
            model = {
              default = function()
                return "gpt-4.1"
              end,
            },
          },
        })
      end,
      xai = function()
        return require("codecompanion.adapters").extend("xai", {
          env = {
            api_key = "cmd:op read op://personal/xAI_API/credential --no-newline",
          },
        })
      end,
      tavily = function()
        return require("codecompanion.adapters").extend("tavily", {
          env = {
            api_key = "cmd:op read op://personal/Tavily_API/credential --no-newline",
          },
        })
      end,
    },
    acp = {
      gemini_cli = function()
        return require("codecompanion.adapters").extend("gemini_cli", {
          defaults = {
            auth_method = "gemini-api-key", -- One of: "gemini-api-key" | "oauth-personal" | | "vertex-ai"
          },
          env = {
            GEMINI_API_KEY = "cmd:op read op://personal/Gemini_API/credential --no-newline",
          },
        })
      end,
      claude_code = function()
        return require("codecompanion.adapters").extend("claude_code", {
          env = {
            ANTHROPIC_API_KEY = "cmd:op read op://personal/Anthropic_API/credential --no-newline",
          },
        })
      end,
    },
  },
  prompt_library = {
    ["Maths tutor"] = {
      strategy = "chat",
      description = "Chat with your personal maths tutor",
      opts = {
        adapter = {
          name = "copilot",
          model = "gpt-4.1",
        },
        index = 4,
        ignore_system_prompt = true,
        default_memory = "maths",
        intro_message = "Welcome to your lesson! How may I help you today? ",
      },
      prompts = {
        {
          role = "system",
          content = [[You are a helpful maths tutor.
You can explain concepts, solve problems, and provide step-by-step solutions for maths.
The user asking the questions has an MPhys in Physics, is knowledgeable in maths, but is out of practice.
The user is an experienced programmer, so you can relate maths concepts to programming ones.
If the user asks you about a topic respond with:
1. A brief explanation of the topic
2. A definition
3. A simple example and a more complex example
4. A programming analogy or example
5. A summary of the topic
6. A question to the user to check their understanding

You must:
- Not use H1 or H2 headings. Only H3 headings and above
- Always show your work and explain each step clearly
- Relate Math concepts to programming terms where applicable
- Use KaTeX for any mathematical notation (as the user works with the Notion app and Anki for flashcards)
- Ensure that any inline KaTeX is within $$ delimiters (e.g. $x^2 + y^2 = z^2$) so it can be pasted into Notion or Anki
- For any math blocks, do NOT use $$ delimiters, just use the math block syntax (e.g. \[ x^2 + y^2 = z^2 \])
- Use Python for coding examples]],
        },
      },
    },
    ["Personal tutor"] = {
      strategy = "chat",
      description = "Chat with your personal tutor",
      opts = {
        adapter = {
          name = "copilot",
          model = "gpt-5-mini",
        },
        index = 4,
        ignore_system_prompt = true,
        intro_message = "Welcome to your lesson! How may I help you today? ",
      },
      prompts = {
        {
          role = "system",
          content = [[You are a helpful and patient Socratic tutor.
Your primary goal is to guide the user to the solution, not just give it to them. You must do this through a turn-based conversation.
You explain things clearly and concisely, assuming the user is a beginner.

When the user asks you to solve a problem, you must follow this exact interaction model:

### Your First Response
1. Acknowledge the question.
2. Ask the user to explain their current understanding of the problem and anything they have already tried.
3. **You must stop here.** End your response by telling the user you will wait for their reply.

### Your Second Response (after the user replies)
1. Thank the user for their input and gently correct any misconceptions.
2. Break the problem down into smaller, manageable parts. Announce the very first part you will tackle.
3. Explain **only** the first part. Keep the explanation simple.
4. **You must stop here.** Ask the user a direct question to confirm they understand (e.g., "Does that first step make sense?"). Tell them you will wait for their reply before proceeding.

### All Subsequent Responses
1. Acknowledge the user's confirmation.
2. Announce and explain the **next single part** of the solution.
3. **You must stop here.** Ask a direct question to confirm understanding and wait for their reply.
4. Repeat this until all parts have been explained.

### Final Response
1. Once the user has understood all the individual parts, provide the complete, assembled solution.
2. Summarise the key takeaways and the overall problem-solving strategy.

### You Must Adhere to These Rules:
- **One Step at a Time:** Never explain more than one part of the problem in a single response.
- **Always Wait:** Your default behavior is to wait for the user. Always end your turn by asking a question and explicitly stating you are waiting.
- **No H1/H2 Headings:** Only use H3 headings and below.
- **Show Your Work:** Explain your reasoning for each step.
- **Override:** If the user responds with `override` at any point, you may provide the full solution immediately.]],
        },
      },
    },
    ["Chain-of-Thought"] = {
      strategy = "workflow",
      description = "Use a CoT workflow to plan and write code",
      opts = {
        adapter = {
          name = "copilot",
          model = "gpt-5",
        },
      },
      prompts = {
        {
          {
            role = "user",
            content = [[DO NOT WRITE ANY CODE YET.

Your task is to act as an expert software architect and create a comprehensive implementation plan.

First, think step-by-step. Then, provide a detailed pseudocode plan that outlines the solution.

Your plan should include:
1.  A high-level summary of the proposed approach.
2.  A breakdown of the required logic into sequential steps.
3.  Identification of any new functions, classes, or components that should be created.
4.  Consideration of how the changes will interact with existing code.
5.  A list of potential edge cases and error conditions to handle.

<!-- Be sure to share any relevant files -->
<!-- Your task here -->]],
            opts = {
              auto_submit = false,
            },
          },
        },
        {
          {
            role = "user",
            content = [[Now, act as a senior technical lead reviewing the previous plan. Your goal is to refine it into a final, highly-detailed specification that another AI can implement flawlessly.

Critically evaluate the plan by answering the following questions:
1.  What are the strengths and weaknesses of the proposed approach?
2.  Are there any alternative approaches? If so, what are their trade-offs?
3.  What potential risks, edge cases, or dependencies did the initial plan miss?
4.  How can the pseudocode be made more specific and closer to the target language's syntax and conventions?

After your analysis, provide a final, revised pseudocode plan. This new plan should incorporate your improvements, be extremely detailed, and leave no room for ambiguity.]],
            opts = {
              adapter = {
                name = "copilot",
                model = "claude-sonnet-4",
              },
              auto_submit = true,
            },
          },
        },
        {
          {
            role = "user",
            content = [[Your task is to write the code based on the final implementation plan that we discussed. Adhere strictly to the plan and do not introduce any new logic.

**Instructions:**
1.  Implement the plan.
2.  Generate only the code. Do not include explanations or conversational text.
3.  Use Markdown code blocks for the code (use 4 backticks instead of 3)
4.  If you are modifying an existing file, include a comment with its path (e.g., `// filepath: src/utils/helpers.js`).
5.  Use comments like `// ...existing code...` to indicate where the new code should be placed within existing files.

**IMPORTANT:**
- Follow the plan exactly.
- Ensure comments are correct for the programming language.]],
            opts = {
              adapter = {
                name = "copilot",
                model = "gpt-4.1",
              },
              auto_submit = true,
            },
          },
        },
      },
    },
    ["Test workflow"] = {
      strategy = "workflow",
      description = "Use a workflow to test the plugin",
      opts = {
        adapter = {
          name = "copilot",
          model = "gpt-4.1",
        },
        index = 4,
      },
      prompts = {
        {
          {
            role = "user",
            content = "Generate a Python class for managing a book library with methods for adding, removing, and searching books",
            opts = {
              auto_submit = false,
            },
          },
        },
        {
          {
            role = "user",
            content = "Write unit tests for the library class you just created",
            opts = {
              auto_submit = true,
            },
          },
        },
        {
          {
            role = "user",
            content = "Create a TypeScript interface for a complex e-commerce shopping cart system",
            opts = {
              auto_submit = true,
            },
          },
        },
        -- {
        --   {
        --     role = "user",
        --     content = "Write a recursive algorithm to balance a binary search tree in Java",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Generate a comprehensive regex pattern to validate email addresses with explanations",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Create a Rust struct and implementation for a thread-safe message queue",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Write a GitHub Actions workflow file for CI/CD with multiple stages",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Create SQL queries for a complex database schema with joins across 4 tables",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Write a Lua configuration for Neovim with custom keybindings and plugins",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
        -- {
        --   {
        --     role = "user",
        --     content = "Generate documentation in JSDoc format for a complex JavaScript API client",
        --     opts = {
        --       auto_submit = true,
        --     },
        --   },
        -- },
      },
    },
  },
  strategies = {
    chat = {
      adapter = {
        name = "copilot",
        model = "gpt-5-mini",
      },
      roles = {
        user = "olimorris",
      },
      keymaps = {
        send = {
          modes = {
            i = { "<C-CR>", "<C-s>" },
          },
        },
        completion = {
          modes = {
            i = "<C-x>",
          },
        },
      },
      slash_commands = {
        ["buffer"] = {
          keymaps = {
            modes = {
              i = "<C-b>",
            },
          },
          opts = {
            provider = "snacks",
          },
        },
        ["fetch"] = {
          keymaps = {
            modes = {
              i = "<C-f>",
            },
          },
        },
        ["file"] = {
          opts = {
            provider = "snacks",
          },
        },
        ["help"] = {
          opts = {
            provider = "snacks",
            max_lines = 1000,
          },
        },
        ["image"] = {
          keymaps = {
            modes = {
              i = "<C-i>",
            },
          },
          opts = {
            dirs = { "~/Documents/Screenshots" },
          },
        },
      },
    },
    inline = {
      adapter = {
        name = "copilot",
        model = "gpt-4.1",
      },
    },
  },
  display = {
    action_palette = {
      provider = "default",
    },
    chat = {
      -- show_references = true,
      -- show_header_separator = false,
      -- show_settings = true,
      fold_context = true,
    },
  },
  memory = {
    opts = {
      chat = {
        enabled = true,
      },
    },
  },
  opts = {
    log_level = "DEBUG",
  },
})
vim.cmd([[cab cc CodeCompanion]])

local spinner = {
  completed = "󰗡 Completed",
  error = " Error",
  cancelled = "󰜺 Cancelled",
}

---Format the adapter name and model for display with the spinner
---@param adapter CodeCompanion.Adapter
---@return string
local function format_adapter(adapter)
  local parts = {}
  table.insert(parts, adapter.formatted_name)
  if adapter.model and adapter.model ~= "" then
    table.insert(parts, "(" .. adapter.model .. ")")
  end
  return table.concat(parts, " ")
end

---Setup the spinner for CodeCompanion
---@return nil
local function codecompanion_spinner()
  local ok, progress = pcall(require, "fidget.progress")
  if not ok then
    return
  end

  spinner.handles = {}

  local group = vim.api.nvim_create_augroup("dotfiles.codecompanion.spinner", {})

  vim.api.nvim_create_autocmd("User", {
    pattern = "CodeCompanionRequestStarted",
    group = group,
    callback = function(args)
      local handle = progress.handle.create({
        title = "",
        message = "  Sending...",
        lsp_client = {
          name = format_adapter(args.data.adapter),
        },
      })
      spinner.handles[args.data.id] = handle
    end,
  })

  vim.api.nvim_create_autocmd("User", {
    pattern = "CodeCompanionRequestFinished",
    group = group,
    callback = function(args)
      local handle = spinner.handles[args.data.id]
      spinner.handles[args.data.id] = nil
      if handle then
        if args.data.status == "success" then
          handle.message = spinner.completed
        elseif args.data.status == "error" then
          handle.message = spinner.error
        else
          handle.message = spinner.cancelled
        end
        handle:finish()
      end
    end,
  })
end

codecompanion_spinner()

require("copilot").setup({
  panel = { enabled = false },
  suggestion = {
    auto_trigger = true, -- Suggest as we start typing
    keymap = {
      accept_word = "<C-l>",
      accept_line = "<C-j>",
    },
  },
})
