{ config, lib, pkgs, globalInputs, system, ... }:

with lib;

let
  cfg = config.home.roles.development;

  # Build beads from flake input with corrected vendorHash
  # The upstream default.nix has stale vendorHash for commits with server mode
  beadsRev = builtins.substring 0 8 (globalInputs.beads.rev or "unknown");
  beadsPackage = pkgs.buildGoModule {
    pname = "beads";
    version = "0.49.1-${beadsRev}";
    src = globalInputs.beads;
    subPackages = [ "cmd/bd" ];
    doCheck = false;
    # Regenerated vendorHash for commit 93965b4a (has dolt server mode, Go 1.24)
    vendorHash = "sha256-gwxGv8y4+1+k0741CnOYcyJPTJ5vTrynqPoO8YS9fbQ=";
    nativeBuildInputs = [ pkgs.git ];
    meta = with lib; {
      description = "beads (bd) - An issue tracker designed for AI-supervised coding workflows";
      homepage = "https://github.com/steveyegge/beads";
      license = licenses.mit;
      mainProgram = "bd";
    };
  };

  # Gastown - multi-agent workspace manager (no upstream flake.nix yet)
  # Source is tracked via flake input for renovate updates
  gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
  gastownPackage = pkgs.buildGoModule {
    pname = "gastown";
    version = "unstable-${gastownRev}";
    src = globalInputs.gastown;
    vendorHash = "sha256-ripY9vrYgVW8bngAyMLh0LkU/Xx1UUaLgmAA7/EmWQU=";
    subPackages = [ "cmd/gt" ];
    doCheck = false;

    # Must match ldflags from gastown Makefile - BuiltProperly=1 is required
    # or gt will error with "This binary was built with 'go build' directly"
    ldflags = [
      "-X github.com/steveyegge/gastown/internal/cmd.Version=${gastownRev}"
      "-X github.com/steveyegge/gastown/internal/cmd.Commit=${gastownRev}"
      "-X github.com/steveyegge/gastown/internal/cmd.BuildTime=nix-build"
      "-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
    ];

    # Bug fixes not yet merged upstream
    # Each patch is stored in a separate file for clarity and maintainability
    patches = [
      # Fix validateRecipient bug: normalize addresses before comparison
      ./gastown-fix-validate-recipient.patch
      # Fix agentBeadToAddress to use title field for hq- prefixed beads
      ./gastown-fix-agent-bead-address-title.patch
      # Fix crew/polecat home paths: remove incorrect /rig suffix
      ./gastown-fix-role-home-paths.patch
      # Fix town root detection: don't map to Mayor (causes spurious mismatch warnings)
      ./gastown-fix-town-root-detection.patch
      # Statusline optimization: skip expensive beads queries for detached sessions
      # Reduces Dolt CPU from ~70% to ~20% by caching and early-exit
      ./gastown-statusline-optimization.patch
    ];

    meta = with lib; {
      description = "Gas Town - multi-agent workspace manager by Steve Yegge";
      homepage = "https://github.com/steveyegge/gastown";
      license = licenses.mit;
      mainProgram = "gt";
    };
  };

  # Perles - TUI for beads issue tracking (no upstream flake.nix yet)
  # Source is tracked via flake input for renovate updates
  perlesRev = builtins.substring 0 8 (globalInputs.perles.rev or "unknown");
  perlesPackage = pkgs.buildGoModule {
    pname = "perles";
    version = "unstable-${perlesRev}";
    src = globalInputs.perles;
    vendorHash = "sha256-JHERJDzbiqgjWXwRhXVjgDEiDQ3AUXRIONotfPF21B0=";
    doCheck = false;

    ldflags = [
      "-X main.version=${perlesRev}"
    ];

    meta = with lib; {
      description = "Perles - Terminal UI for beads issue tracking";
      homepage = "https://github.com/zjrosen/perles";
      license = licenses.mit;
      mainProgram = "perles";
    };
  };

  # Fetch the claude-plugins repository (for humanlayer commands/agents)
  # Update the rev to get newer versions of the commands
  claudePluginsRepo = builtins.fetchGit {
    url = "https://github.com/jeffh/claude-plugins.git";
    # To update: change this to the latest commit hash
    # You can find the latest commit at: https://github.com/jeffh/claude-plugins/commits/main
    rev = "5e3e4d937162185b6d78c62022cbfd1c8ad42c4c";
    ref = "main";
  };

in
{
  options.home.roles.development = {
    enable = mkEnableOption "Enable development tools and utilities";

    allowArbitraryClaudeCodeModelSelection = mkOption {
      type = types.bool;
      default = false;
      description = ''
        Whether to preserve model specifications in Claude Code humanlayer commands and agents.

        When false (default), the model: line is stripped from frontmatter, allowing Claude Code
        to use its default model selection.

        When true, the model: specifications from the source files are preserved, allowing
        commands to specify opus/sonnet/haiku explicitly.
      '';
    };
  };

  config = mkIf cfg.enable {
    home.packages = [
      beadsPackage
      gastownPackage
      perlesPackage
      pkgs.unstable.claude-code
      pkgs.unstable.claude-code-router
      pkgs.unstable.codex
      pkgs.dolt
      pkgs.sqlite

      # Custom packages
      pkgs.custom.tea-rbw
    ];

    # Install Claude Code humanlayer command and agent plugins
    home.activation.claudeCodeCommands = lib.hm.dag.entryAfter ["writeBoundary"] ''
      # Clean up old plugin-installed commands and agents to avoid duplicates
      rm -f ~/.claude/commands/humanlayer:* 2>/dev/null || true
      rm -f ~/.claude/agents/humanlayer:* 2>/dev/null || true

      # Create directories if they don't exist
      mkdir -p ~/.claude/commands
      mkdir -p ~/.claude/agents

      # Copy all humanlayer command files and remove model specifications
      for file in ${claudePluginsRepo}/humanlayer/commands/*.md; do
        if [ -f "$file" ]; then
          filename=$(basename "$file" .md)
          dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
          rm -f "$dest" 2>/dev/null || true

          # Copy file and conditionally remove the "model:" line from frontmatter
          ${if cfg.allowArbitraryClaudeCodeModelSelection
            then "cp \"$file\" \"$dest\""
            else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
          }
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      # Copy all humanlayer agent files and remove model specifications
      for file in ${claudePluginsRepo}/humanlayer/agents/*.md; do
        if [ -f "$file" ]; then
          filename=$(basename "$file" .md)
          dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
          rm -f "$dest" 2>/dev/null || true

          # Copy file and conditionally remove the "model:" line from frontmatter
          ${if cfg.allowArbitraryClaudeCodeModelSelection
            then "cp \"$file\" \"$dest\""
            else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
          }
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      # Copy local commands from this repo (with retry for race conditions with running Claude)
      for file in ${./commands}/*.md; do
        if [ -f "$file" ]; then
          filename=$(basename "$file" .md)
          dest="$HOME/.claude/commands/''${filename}.md"
          # Remove existing file first, then copy with retry on failure
          rm -f "$dest" 2>/dev/null || true
          if ! cp "$file" "$dest" 2>/dev/null; then
            sleep 0.5
            cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to commands"
          fi
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      # Copy local skills (reference materials) to skills subdirectory
      mkdir -p ~/.claude/commands/skills
      for file in ${./skills}/*.md; do
        if [ -f "$file" ]; then
          filename=$(basename "$file" .md)
          dest="$HOME/.claude/commands/skills/''${filename}.md"
          rm -f "$dest" 2>/dev/null || true
          if ! cp "$file" "$dest" 2>/dev/null; then
            sleep 0.5
            cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to skills"
          fi
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      # Copy micro-skills (compact reusable knowledge referenced by formulas)
      for file in ${./skills/micro}/*.md; do
        if [ -f "$file" ]; then
          dest="$HOME/.claude/commands/skills/$(basename "$file")"
          rm -f "$dest" 2>/dev/null || true
          cp "$file" "$dest"
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      # Install beads formulas to user-level formula directory
      mkdir -p ~/.beads/formulas
      for file in ${./formulas}/*.formula.toml; do
        if [ -f "$file" ]; then
          dest="$HOME/.beads/formulas/$(basename "$file")"
          rm -f "$dest" 2>/dev/null || true
          cp "$file" "$dest"
          chmod u+w "$dest" 2>/dev/null || true
        fi
      done

      $DRY_RUN_CMD echo "Claude Code plugins installed: humanlayer commands/agents + local commands + local skills + formulas"
    '';

    # Set up beads Claude Code integration (hooks for SessionStart/PreCompact)
    # This uses the CLI + hooks approach which is recommended over MCP for Claude Code
    home.activation.claudeCodeBeadsSetup = lib.hm.dag.entryAfter ["writeBoundary" "claudeCodeCommands"] ''
      # Run bd setup claude to install hooks into ~/.claude/settings.json
      # This is idempotent - safe to run multiple times
      ${beadsPackage}/bin/bd setup claude 2>/dev/null || true

      $DRY_RUN_CMD echo "Claude Code beads integration configured (hooks installed)"
    '';

    # Beads timer gate checker (Linux only - uses systemd)
    # Runs every 5 minutes to auto-resolve expired timer gates across all beads projects
    # This enables self-scheduling molecules (watchers, patrols, etc.)
    systemd.user.services.beads-gate-check = lib.mkIf pkgs.stdenv.isLinux {
      Unit = {
        Description = "Check and resolve expired beads timer gates";
      };
      Service = {
        Type = "oneshot";
        # Check gates in all workspaces that have running daemons
        ExecStart = pkgs.writeShellScript "beads-gate-check-all" ''
          # Get list of workspaces from daemon registry
          workspaces=$(${beadsPackage}/bin/bd daemon list --json 2>/dev/null | ${pkgs.jq}/bin/jq -r '.[].workspace // empty' 2>/dev/null)

          if [ -z "$workspaces" ]; then
            exit 0  # No beads workspaces, nothing to do
          fi

          for ws in $workspaces; do
            if [ -d "$ws" ]; then
              cd "$ws" && ${beadsPackage}/bin/bd gate check --type=timer --quiet 2>/dev/null || true
            fi
          done
        '';
      };
    };

    systemd.user.timers.beads-gate-check = lib.mkIf pkgs.stdenv.isLinux {
      Unit = {
        Description = "Periodic beads timer gate check";
      };
      Timer = {
        OnBootSec = "5min";
        OnUnitActiveSec = "5min";
      };
      Install = {
        WantedBy = [ "timers.target" ];
      };
    };

    # Note: modules must be imported at top-level home config
  };
}
