/**
 * Agent Mode Management Tools
 *
 * Tools for entering and exiting modes, following Claude Code's approach
 * of explicit mode transitions with user consent.
 */

import { z } from 'zod';
import { defineTool, type ToolResult } from './types.js';

/**
 * Tool mode values
 */
export type ToolModeValue = 'plan' | 'code';

/**
 * Supported modes that can be entered via enter_mode tool
 */
export const SUPPORTED_MODES: ToolModeValue[] = ['plan'];

/**
 * Exploration strategies for planning
 */
export const STRATEGIES = {
  understand_feature: {
    description: 'Understand how a feature works',
    steps: [
      { tool: 'semantic_search', purpose: 'Find entry points' },
      { tool: 'expand_node', purpose: 'Understand main components' },
      { tool: 'get_callers', purpose: 'See usage patterns' },
      { tool: 'get_file_dependencies', purpose: 'Map module structure' },
    ],
  },
  debug_issue: {
    description: 'Debug a bug or issue',
    steps: [
      { tool: 'grep', purpose: 'Find error messages or symptoms' },
      { tool: 'semantic_search', purpose: 'Find related code' },
      { tool: 'get_callees', purpose: 'Trace execution path' },
      { tool: 'expand_node', purpose: 'Examine suspicious code' },
    ],
  },
  assess_change_impact: {
    description: 'Assess impact of a proposed change',
    steps: [
      { tool: 'get_callers', purpose: 'Find all usages' },
      { tool: 'get_impact_analysis', purpose: 'Assess blast radius' },
      { tool: 'get_file_dependencies', purpose: 'Find affected modules' },
      { tool: 'get_class_hierarchy', purpose: 'Check inheritance impact' },
    ],
  },
  onboard_codebase: {
    description: 'Get oriented in a new codebase',
    steps: [
      { tool: 'get_communities', purpose: 'Understand major areas' },
      { tool: 'get_top_pagerank', purpose: 'Find key components' },
      { tool: 'get_area_importance', purpose: 'Map directory structure' },
      { tool: 'semantic_search', purpose: 'Explore specific topics' },
    ],
  },
  find_similar_code: {
    description: 'Find code similar to a reference',
    steps: [
      { tool: 'expand_node', purpose: 'Understand the reference' },
      { tool: 'semantic_search', purpose: 'Find similar patterns' },
      { tool: 'get_neighbors', purpose: 'Explore related code' },
    ],
  },
} as const;

export type StrategyName = keyof typeof STRATEGIES;

/**
 * Exploration step in a strategy
 */
export interface ExplorationStep {
  tool: string;
  purpose: string;
}

/**
 * ModeState - Singleton for managing agent mode state
 */
export class ModeState {
  private static instance: ModeState | null = null;

  currentMode: ToolModeValue = 'code';
  planContent: string | null = null;
  planSubmitted = false;
  planModeRequested = false;
  planModeReason: string | null = null;
  planFilePath: string | null = null;

  private constructor() {}

  /**
   * Get the singleton instance
   */
  static getInstance(): ModeState {
    if (!ModeState.instance) {
      ModeState.instance = new ModeState();
    }
    return ModeState.instance;
  }

  /**
   * Reset the singleton instance
   */
  static reset(): void {
    ModeState.instance = null;
  }

  /**
   * Reset per-cycle state (called on new user message)
   */
  resetCycle(): void {
    this.planSubmitted = false;
    this.planModeRequested = false;
    this.planModeReason = null;
  }

  /**
   * Approve plan mode entry (called when user approves)
   */
  approvePlanMode(): void {
    this.currentMode = 'plan';
    this.planContent = null;
    this.planModeRequested = false;
    this.planModeReason = null;
  }

  /**
   * Reject plan mode entry (called when user rejects)
   */
  rejectPlanMode(): void {
    this.planModeRequested = false;
    this.planModeReason = null;
    this.planFilePath = null;
  }

  /**
   * Set the plan file path (called when entering plan mode)
   */
  setPlanFilePath(path: string): void {
    this.planFilePath = path;
  }

  /**
   * Get the current plan file path
   */
  getPlanFilePath(): string | null {
    return this.planFilePath;
  }

  /**
   * Exit plan mode and return to code mode
   */
  exitPlanMode(): void {
    this.currentMode = 'code';
    this.planFilePath = null;
  }
}

/**
 * Get the current mode state instance
 */
export function getModeState(): ModeState {
  return ModeState.getInstance();
}

// ============================================================
// Tool Definitions
// ============================================================

/**
 * EnterPlanMode Tool
 *
 * Request to enter plan mode - REQUIRES USER CONSENT.
 * Following Claude Code's pattern where entering plan mode is a proposal
 * that requires user approval, not an automatic switch.
 */
export const enterPlanModeTool = defineTool({
  name: 'enter_plan_mode',
  description: `Request to enter plan mode for implementation planning.

This tool REQUIRES USER APPROVAL before plan mode is activated.

Use this proactively when you're about to start a non-trivial implementation task.
Getting user sign-off on your approach before writing code prevents wasted effort.

When to use:
- New feature implementation requiring architectural decisions
- Multiple valid approaches exist (user should choose)
- Multi-file changes expected (more than 2-3 files)
- Unclear requirements that need exploration first

When NOT to use:
- Single-line or few-line fixes
- Trivial tasks with obvious implementation
- Pure research/exploration (just explore directly)
- Tasks with very specific, detailed instructions already provided

The user will see your reason and can approve or reject entering plan mode.`,
  category: 'planning',
  schema: z.object({
    reason: z.string().min(1).describe('Why you want to enter plan mode (explain the task complexity)'),
  }),
  async execute(input): Promise<ToolResult<{
    status: string;
    reason: string;
    message: string;
  }>> {
    const { reason } = input;
    const state = getModeState();

    if (state.currentMode === 'plan') {
      return {
        success: false,
        error: 'Already in plan mode',
        suggestions: ['Use exit_plan to submit your plan for approval'],
      };
    }

    // Check if already requested this cycle
    if (state.planModeRequested) {
      return {
        success: false,
        error: 'Plan mode already requested. Wait for user response.',
        suggestions: ['Do not call enter_plan_mode again until user responds.'],
      };
    }

    // Mark as requested (not entered - user must approve)
    state.planModeRequested = true;
    state.planModeReason = reason.trim();

    return {
      success: true,
      data: {
        status: 'plan_mode_requested',
        reason: reason.trim(),
        message: 'Plan mode requested. Waiting for user approval.',
      },
    };
  },
});

/**
 * ExitPlan Tool
 *
 * Submit an implementation plan for user approval.
 */
export const exitPlanTool = defineTool({
  name: 'exit_plan',
  description: `Submit an implementation plan for user approval.

Use this tool to present a plan to the user for approval. The plan can come from:
1. A Plan sub-agent you spawned via task(subagent_type="Plan", ...)
2. Your own planning (if in plan mode)

Pass the plan content as the 'plan' parameter.

The user will either:
- Approve: You can proceed with implementation
- Reject: You'll receive feedback and can revise`,
  category: 'planning',
  schema: z.object({
    plan: z.string().optional().describe('The implementation plan as markdown. If not provided in plan mode, reads from the plan file.'),
  }),
  async execute(input): Promise<ToolResult<{
    status: string;
    plan: string;
    message: string;
  }>> {
    const { plan } = input;
    const state = getModeState();

    // Prevent multiple exit_plan calls per cycle
    if (state.planSubmitted) {
      return {
        success: false,
        error: 'Plan already submitted. Wait for user approval.',
        suggestions: ['Do not call exit_plan again until user responds.'],
      };
    }

    let planContent = plan;

    // In plan mode, try to read from plan file if not provided
    if (state.currentMode === 'plan') {
      if (!planContent?.trim()) {
        const planFilePath = state.getPlanFilePath();
        if (planFilePath) {
          try {
            const { existsSync, readFileSync } = await import('node:fs');
            if (existsSync(planFilePath)) {
              planContent = readFileSync(planFilePath, 'utf-8');
            }
          } catch (e) {
            return {
              success: false,
              error: `Failed to read plan file: ${e}`,
              suggestions: [`Write your plan to ${planFilePath} first`],
            };
          }
        }
      }
    }

    // In code mode, plan content is required (from Plan subagent)
    if (state.currentMode === 'code') {
      if (!planContent?.trim()) {
        return {
          success: false,
          error: 'Plan content is required when submitting from code mode',
          suggestions: [
            "Pass the plan from your Plan sub-agent as the 'plan' parameter",
            'Example: exit_plan(plan=<plan_from_subagent>)',
          ],
        };
      }
    }

    if (!planContent?.trim()) {
      const planFilePath = state.getPlanFilePath() || 'the plan file';
      return {
        success: false,
        error: 'Plan content is required',
        suggestions: [
          `Write your plan to ${planFilePath} using write_to_file, then call exit_plan`,
          'Or pass the plan directly as a parameter',
        ],
      };
    }

    // Store plan content for reference and mark as submitted
    state.planContent = planContent.trim();
    state.planSubmitted = true;

    return {
      success: true,
      data: {
        status: 'plan_submitted',
        plan: planContent.trim(),
        message: 'Plan submitted for user approval. Waiting for user response.',
      },
    };
  },
});

/**
 * GetMode Tool
 *
 * Get the current agent operating mode.
 */
export const getModeTool = defineTool({
  name: 'get_mode',
  description: 'Get the current agent operating mode (plan or code).',
  category: 'planning',
  schema: z.object({}),
  async execute(): Promise<ToolResult<{
    current_mode: ToolModeValue;
    has_plan: boolean;
    available_modes: ToolModeValue[];
  }>> {
    const state = getModeState();

    return {
      success: true,
      data: {
        current_mode: state.currentMode,
        has_plan: state.planContent !== null,
        available_modes: SUPPORTED_MODES,
      },
    };
  },
});

/**
 * PlanExploration Tool
 *
 * Create a structured exploration plan for understanding code.
 */
export const planExplorationTool = defineTool({
  name: 'plan_exploration',
  description: `Create a structured exploration plan for understanding code.

Available strategies:
- understand_feature: Learn how a feature works
- debug_issue: Track down a bug
- assess_change_impact: Evaluate change risk
- onboard_codebase: Get oriented in new code
- find_similar_code: Find similar patterns

Or provide a custom goal and get a tailored plan.`,
  category: 'planning',
  schema: z.object({
    goal: z.string().min(1).describe('What you want to understand or accomplish'),
    strategy: z.enum(['understand_feature', 'debug_issue', 'assess_change_impact', 'onboard_codebase', 'find_similar_code']).optional().describe('Predefined strategy to use'),
    context: z.string().optional().describe('Additional context about the goal'),
    constraints: z.array(z.string()).optional().describe('Constraints to consider'),
    use_case: z.enum(['spec', 'debug', 'review', 'onboard']).optional().describe('Use case hint to guide strategy selection'),
  }),
  async execute(input): Promise<ToolResult<{
    goal: string;
    strategy: string;
    description: string;
    steps: ExplorationStep[];
    context?: string;
    constraints?: string[];
    inferred?: boolean;
    available_strategies?: string[];
  }>> {
    const { goal, context, constraints, use_case } = input;
    let { strategy } = input;

    try {
      // Map use_case to strategy if strategy not provided
      if (use_case && !strategy) {
        const useCaseMapping: Record<string, StrategyName> = {
          spec: 'understand_feature',
          debug: 'debug_issue',
          review: 'assess_change_impact',
          onboard: 'onboard_codebase',
        };
        strategy = useCaseMapping[use_case];
      }

      // Use predefined strategy if specified
      if (strategy && strategy in STRATEGIES) {
        const strat = STRATEGIES[strategy as StrategyName];
        return {
          success: true,
          data: {
            goal,
            strategy,
            description: strat.description,
            steps: [...strat.steps],
            context,
            constraints,
          },
        };
      }

      // Infer strategy from goal
      const inferred = inferStrategy(goal);

      if (inferred && inferred in STRATEGIES) {
        const strat = STRATEGIES[inferred];
        return {
          success: true,
          data: {
            goal,
            strategy: inferred,
            description: strat.description,
            steps: [...strat.steps],
            context,
            constraints,
            inferred: true,
          },
        };
      }

      // Generic exploration plan
      return {
        success: true,
        data: {
          goal,
          strategy: 'custom',
          description: 'Custom exploration plan',
          steps: [
            { tool: 'semantic_search', purpose: `Search for '${goal}'` },
            { tool: 'expand_node', purpose: 'Examine top results' },
            { tool: 'get_neighbors', purpose: 'Explore connections' },
          ],
          context,
          constraints,
          available_strategies: Object.keys(STRATEGIES),
        },
      };
    } catch (e) {
      return {
        success: false,
        error: `Planning failed: ${e}`,
      };
    }
  },
});

/**
 * Infer strategy from goal text
 */
function inferStrategy(goal: string): StrategyName | null {
  const goalLower = goal.toLowerCase();

  if (['bug', 'error', 'issue', 'fix', 'debug'].some(word => goalLower.includes(word))) {
    return 'debug_issue';
  }

  if (['how', 'understand', 'learn', 'feature'].some(word => goalLower.includes(word))) {
    return 'understand_feature';
  }

  if (['change', 'modify', 'refactor', 'impact'].some(word => goalLower.includes(word))) {
    return 'assess_change_impact';
  }

  if (['new', 'onboard', 'overview', 'structure'].some(word => goalLower.includes(word))) {
    return 'onboard_codebase';
  }

  if (['similar', 'like', 'pattern', 'example'].some(word => goalLower.includes(word))) {
    return 'find_similar_code';
  }

  return null;
}

/**
 * All mode/planning tools
 */
export const modeTools = [
  enterPlanModeTool,
  exitPlanTool,
  getModeTool,
  planExplorationTool,
];
