/**
 * Planning API Routes
 *
 * Endpoints for code exploration and planning.
 */

import { Hono } from 'hono';
import { z } from 'zod';
import type { AppContext } from '../router.js';
import { ValidationError } from '../middleware/error.js';
import {
  ContextBuilder,
  FeatureContextBuilder,
  SimilaritySearch,
  PlannerAgent,
  getModeStateManager,
  PlanContextRequestSchema,
  SimilarSearchRequestSchema,
  AgentMode,
} from '../../planning/index.js';

/**
 * Request schemas
 */
const FeatureContextRequestSchema = z.object({
  query: z.string().min(1),
  options: z.object({
    maxRootEntities: z.number().positive().optional(),
    maxCallGraphHops: z.number().positive().optional(),
    maxDependencyHops: z.number().positive().optional(),
    includeTests: z.boolean().optional(),
  }).optional(),
});

const ResearchPlanRequestSchema = z.object({
  goal: z.string().min(1),
  budget: z.object({
    maxToolCalls: z.number().positive().optional(),
    maxTokens: z.number().positive().optional(),
    maxTimeMinutes: z.number().positive().optional(),
  }).optional(),
});

const EnterPlanModeRequestSchema = z.object({
  workDir: z.string().min(1),
});

const WritePlanRequestSchema = z.object({
  content: z.string().min(1),
});

const RejectPlanRequestSchema = z.object({
  feedback: z.string().min(1),
});

/**
 * Create planning routes
 */
export function planRoutes(): Hono<{ Variables: AppContext }> {
  const router = new Hono<{ Variables: AppContext }>();

  /**
   * POST /plan/context - Build planning context
   */
  router.post('/context', async (c) => {
    const body = await c.req.json();
    const result = PlanContextRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { query, options } = result.data;
    const conn = c.get('db');
    const embeddings = c.get('embeddings');

    const builder = new ContextBuilder(conn, embeddings);
    const context = await builder.buildContext(query, options);

    return c.json({
      success: true,
      context,
    });
  });

  /**
   * POST /plan/feature - Build feature context with graphs
   */
  router.post('/feature', async (c) => {
    const body = await c.req.json();
    const result = FeatureContextRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { query, options } = result.data;
    const conn = c.get('db');
    const embeddings = c.get('embeddings');

    const builder = new FeatureContextBuilder(conn, embeddings);
    const context = await builder.buildFeatureContext(query, options);

    // Convert Maps to objects for JSON serialization
    return c.json({
      success: true,
      context: {
        ...context,
        callGraph: Object.fromEntries(context.callGraph),
        dependencyGraph: Object.fromEntries(context.dependencyGraph),
      },
    });
  });

  /**
   * POST /plan/similar - Find similar code
   */
  router.post('/similar', async (c) => {
    const body = await c.req.json();
    const result = SimilarSearchRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { query, type, limit, minScore } = result.data;
    const conn = c.get('db');
    const embeddings = c.get('embeddings');

    if (type === 'code') {
      const search = new SimilaritySearch(conn, embeddings);
      const results = await search.findSimilarCode(query, { limit, minScore });
      return c.json({
        success: true,
        results,
      });
    } else {
      // PR search - placeholder
      return c.json({
        success: true,
        results: [],
        message: 'PR search not yet implemented',
      });
    }
  });

  /**
   * POST /plan/research - Create a research plan
   */
  router.post('/research', async (c) => {
    const body = await c.req.json();
    const result = ResearchPlanRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { goal, budget } = result.data;
    const conn = c.get('db');
    const embeddings = c.get('embeddings');

    const planner = new PlannerAgent(conn, embeddings);
    const plan = await planner.createResearchPlan(goal, budget);
    const validation = planner.validatePlan(plan);
    const effort = planner.estimatePlanEffort(plan);

    return c.json({
      success: true,
      plan,
      validation,
      effort,
    });
  });

  /**
   * GET /plan/mode - Get current mode state
   */
  router.get('/mode', (c) => {
    const manager = getModeStateManager();
    const state = manager.getState();

    return c.json({
      success: true,
      state: {
        mode: state.mode,
        planPath: state.planPath,
        planSubmitted: state.planSubmitted,
        hasPlan: !!state.planContent,
        rejectionFeedback: state.rejectionFeedback,
      },
    });
  });

  /**
   * POST /plan/mode/enter - Enter plan mode
   */
  router.post('/mode/enter', async (c) => {
    const body = await c.req.json();
    const result = EnterPlanModeRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { workDir } = result.data;
    const manager = getModeStateManager();
    const state = await manager.enterPlanMode(workDir);

    return c.json({
      success: true,
      state: {
        mode: state.mode,
        planPath: state.planPath,
        planSubmitted: state.planSubmitted,
      },
    });
  });

  /**
   * POST /plan/mode/exit - Exit plan mode
   */
  router.post('/mode/exit', (c) => {
    const manager = getModeStateManager();
    const state = manager.exitPlanMode();

    return c.json({
      success: true,
      state: {
        mode: state.mode,
      },
    });
  });

  /**
   * POST /plan/write - Write plan content
   */
  router.post('/write', async (c) => {
    const body = await c.req.json();
    const result = WritePlanRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { content } = result.data;
    const manager = getModeStateManager();

    if (!manager.isInPlanMode()) {
      throw new ValidationError('Not in plan mode');
    }

    await manager.writePlan(content);

    return c.json({
      success: true,
      planPath: manager.getPlanPath(),
    });
  });

  /**
   * GET /plan/content - Get current plan content
   */
  router.get('/content', (c) => {
    const manager = getModeStateManager();

    if (!manager.isInPlanMode()) {
      return c.json({
        success: true,
        content: null,
        message: 'Not in plan mode',
      });
    }

    return c.json({
      success: true,
      content: manager.getPlanContent(),
      planPath: manager.getPlanPath(),
    });
  });

  /**
   * POST /plan/submit - Submit plan for approval
   */
  router.post('/submit', (c) => {
    const manager = getModeStateManager();

    if (!manager.isInPlanMode()) {
      throw new ValidationError('Not in plan mode');
    }

    manager.submitPlan();

    return c.json({
      success: true,
      planContent: manager.getPlanContent(),
      planPath: manager.getPlanPath(),
      message: 'Plan submitted for approval',
    });
  });

  /**
   * POST /plan/approve - Approve plan and exit to code mode
   */
  router.post('/approve', (c) => {
    const manager = getModeStateManager();

    if (!manager.isInPlanMode()) {
      throw new ValidationError('Not in plan mode');
    }

    const state = manager.approvePlan();

    return c.json({
      success: true,
      planPath: state.planPath,
      message: 'Plan approved, switched to code mode',
    });
  });

  /**
   * POST /plan/reject - Reject plan with feedback
   */
  router.post('/reject', async (c) => {
    const body = await c.req.json();
    const result = RejectPlanRequestSchema.safeParse(body);

    if (!result.success) {
      throw new ValidationError('Invalid request', result.error.errors);
    }

    const { feedback } = result.data;
    const manager = getModeStateManager();

    if (!manager.isInPlanMode()) {
      throw new ValidationError('Not in plan mode');
    }

    manager.rejectPlan(feedback);

    return c.json({
      success: true,
      message: 'Plan rejected, please revise based on feedback',
      feedback,
    });
  });

  return router;
}
