import { readFileSync } from "node:fs";
import type { PilotDeckElicitationAnswer, PilotDeckElicitationRequest } from "../elicitation/PilotDeckElicitationChannel.js";
import { PilotDeckToolRuntimeError } from "../protocol/errors.js";
import type { PilotDeckToolDefinition } from "../protocol/types.js";

export type ExitPlanModeInput = {
  plan_file_path: string;
};
export type ExitPlanModeOutput = {
  plan: string;
  requestedMode?: "default";
  action?: "continue_planning" | "execute_plan" | "cancelled";
  feedback?: string;
  planFilePath?: string;
  planTitle?: string;
  planSummary?: string;
};

const EXIT_PLAN_MODE_QUESTION = "What should happen next?";
const EXIT_PLAN_MODE_CONTINUE = "continue_planning";
const EXIT_PLAN_MODE_EXECUTE = "execute_plan";

const ENTER_PLAN_MODE_DESCRIPTION =
  "Enter plan mode for complex tasks requiring exploration and design. " +
  "Switches to a read-only phase where you explore the codebase, understand patterns, " +
  "and write a structured plan to a plan file before making any changes. " +
  "Prefer using this tool for non-trivial implementation tasks, especially when: " +
  "multiple valid approaches exist, the task touches many files, " +
  "or requirements need exploration to fully understand. " +
  "Do NOT call this tool if you are already in plan mode.";

const EXIT_PLAN_MODE_DESCRIPTION =
  "Signal that your plan is complete and ready for user review. " +
  "Pass the plan_file_path for the markdown plan you want to submit from `.pilotdeck/plans`. " +
  "Do NOT use ask_user_question to ask about plan approval — that is exactly what this tool does.";

function buildEnterPlanModeResult(planDirectoryPath: string | undefined): string {
  const planDirectorySection = planDirectoryPath
    ? `## Plan Directory\nCreate your plan as a markdown file under: ${planDirectoryPath}\nYou may name the file yourself, but it must live under this directory.\n`
    : "";

  return [
    "Plan mode activated. You are now in a read-only exploration and planning phase.",
    "",
    planDirectorySection,
    "## What To Do",
    "1. Explore the codebase using read_file, grep, glob to understand existing patterns and structure",
    "2. Identify the key files, functions, and data flows relevant to the task",
    "3. Design your implementation approach — consider trade-offs between alternatives",
    ...(planDirectoryPath
      ? [
          "4. Create and refine your own markdown plan file under the plan directory above",
          "5. When your plan is ready, call exit_plan_mode with the plan_file_path you want to submit",
        ]
      : ["4. When your plan is ready, call exit_plan_mode to present it for user approval"]),
    "",
    "## Rules",
    `- DO NOT call write_file, edit_file, or bash (non-readonly) on any file${planDirectoryPath ? " except markdown plan files under the designated plan directory" : ""}`,
    "- You MAY use ask_user_question to clarify requirements or choose between approaches",
    "- Focus on understanding before proposing — read first, plan second",
  ].join("\n");
}

function buildAlreadyInPlanModeResult(planDirectoryPath: string | undefined): string {
  return [
    "Plan mode is already active.",
    "",
    ...(planDirectoryPath
      ? [
          `Your plan directory is: ${planDirectoryPath}`,
          "Continue refining markdown plan files under that directory, then submit one explicitly with exit_plan_mode(plan_file_path).",
          "",
        ]
      : []),
    "Stay in read-only exploration/planning until you are ready to call exit_plan_mode.",
  ].join("\n");
}

function buildApprovedPlanResult(plan: string, planFilePath: string | undefined): string {
  const locationSection = planFilePath
    ? [
        `Submitted plan file: ${planFilePath}`,
        "You can refer back to it during implementation if needed.",
        "",
      ]
    : [];
  return [
    "User has approved your plan. You can now start coding.",
    'Do NOT output any confirmation text like "Plan approved, starting implementation" — the user already knows. Proceed directly with todo_write and implementation.',
    "Before using any non-read-only tool, you MUST call todo_write with a markdown checklist derived from the approved plan.",
    "After each completed implementation step, call todo_write again to refresh the checklist and mark completed items with `- [x]`.",
    "",
    ...locationSection,
    "## Approved Plan",
    plan,
  ].join("\n");
}

function buildContinuePlanningResult(feedback: string | undefined): string {
  const feedbackSection = feedback
    ? `\n\nUser feedback:\n${feedback}`
    : "\n\nNo additional feedback was provided.";
  return [
    "The user wants to continue planning before implementation.",
    "Stay in plan mode, refine the plan file, and call exit_plan_mode again when the updated plan is ready.",
  ].join(" ") + feedbackSection;
}

function getExitPlanFeedback(answer: PilotDeckElicitationAnswer): string | undefined {
  if (answer.type !== "answered" || !answer.annotations) {
    return undefined;
  }
  for (const annotation of Object.values(answer.annotations)) {
    if (annotation?.notes?.trim()) {
      return annotation.notes.trim();
    }
  }
  return undefined;
}

function getExitPlanAction(answer: PilotDeckElicitationAnswer): "continue_planning" | "execute_plan" | undefined {
  if (answer.type !== "answered") {
    return undefined;
  }
  for (const value of Object.values(answer.answers)) {
    if (Array.isArray(value)) {
      const action = value.find((entry) => entry === EXIT_PLAN_MODE_CONTINUE || entry === EXIT_PLAN_MODE_EXECUTE);
      if (action) return action;
      continue;
    }
    if (value === EXIT_PLAN_MODE_CONTINUE || value === EXIT_PLAN_MODE_EXECUTE) {
      return value;
    }
  }
  return undefined;
}

export function createEnterPlanModeTool(): PilotDeckToolDefinition<Record<string, never>> {
  return {
    name: "enter_plan_mode",
    aliases: ["EnterPlanMode"],
    description: ENTER_PLAN_MODE_DESCRIPTION,
    kind: "session",
    inputSchema: {
      type: "object",
      additionalProperties: false,
      properties: {},
    },
    isReadOnly: () => true,
    isConcurrencySafe: () => true,
    execute: async (_input, context) => {
      if (context?.permissionMode === "plan") {
        throw new PilotDeckToolRuntimeError(
          "tool_execution_failed",
          buildAlreadyInPlanModeResult(context?.planDirectory?.path),
        );
      }
      const text = buildEnterPlanModeResult(context?.planDirectory?.path);
      return {
        content: [{ type: "text", text }],
        data: { requestedMode: "plan" },
      };
    },
  };
}

export function createExitPlanModeTool(): PilotDeckToolDefinition<ExitPlanModeInput, ExitPlanModeOutput> {
  return {
    name: "exit_plan_mode",
    aliases: ["ExitPlanMode"],
    description: EXIT_PLAN_MODE_DESCRIPTION,
    kind: "session",
    inputSchema: {
      type: "object",
      required: ["plan_file_path"],
      additionalProperties: false,
      properties: {
        plan_file_path: {
          type: "string",
          description: "Path to the markdown plan file to submit from the current project's `.pilotdeck/plans` directory.",
        },
      },
    },
    isReadOnly: () => true,
    isConcurrencySafe: () => true,
    requiresUserInteraction: () => true,
    execute: async (input, context) => {
      const channel = context?.elicitation;
      if (!channel) {
        throw new PilotDeckToolRuntimeError(
          "unsupported_tool",
          "exit_plan_mode requires a connected user interaction channel.",
        );
      }
      const resolvedPlanFilePath = context?.planDirectory?.resolve(input.plan_file_path);
      if (!resolvedPlanFilePath) {
        throw new PilotDeckToolRuntimeError(
          "invalid_tool_input",
          "plan_file_path must point to a markdown file under the current project's .pilotdeck/plans directory.",
        );
      }
      let plan: string;
      try {
        plan = readFileSync(resolvedPlanFilePath, "utf8").trim();
      } catch {
        throw new PilotDeckToolRuntimeError(
          "invalid_tool_input",
          `Plan file does not exist or could not be read: ${resolvedPlanFilePath}`,
        );
      }
      if (!plan) {
        throw new PilotDeckToolRuntimeError(
          "invalid_tool_input",
          "Plan file is empty. Write your plan first before calling exit_plan_mode.",
        );
      }
      const request: PilotDeckElicitationRequest = {
        toolCallId: context.turnId,
        toolName: "exit_plan_mode",
        previewFormat: "markdown",
        questions: [
          {
            question: EXIT_PLAN_MODE_QUESTION,
            header: "Plan",
            options: [
              {
                label: EXIT_PLAN_MODE_CONTINUE,
                description: "Keep planning and update the plan before any implementation starts.",
              },
              {
                label: EXIT_PLAN_MODE_EXECUTE,
                description: "Leave plan mode and let the agent execute this plan.",
              },
            ],
          },
        ],
        metadata: {
          source: "exit_plan_mode",
          plan,
          planFilePath: resolvedPlanFilePath,
        },
        ...(context.abortSignal ? { signal: context.abortSignal } : {}),
      };
      const answer = await channel.askUser(request);
      const action = getExitPlanAction(answer);
      const feedback = getExitPlanFeedback(answer);

      if (answer.type === "cancelled" || !action) {
        return {
          content: [{
            type: "text",
            text: "Exit plan mode was cancelled. Stay in plan mode and continue refining the plan file.",
          }],
          data: { plan, action: "cancelled" },
        };
      }

      if (action === EXIT_PLAN_MODE_EXECUTE) {
        context.planTodo?.markPlanApproved(plan);
        const titleMatch = plan.match(/^#\s+(.+)$/m);
        const planTitle = titleMatch?.[1];
        const summaryLines = plan.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
        const planSummary = summaryLines.slice(0, 2).join("\n").slice(0, 200) || undefined;
        return {
          content: [{
            type: "text",
            text: buildApprovedPlanResult(plan, resolvedPlanFilePath),
          }],
          data: { plan, action, requestedMode: "default", planFilePath: resolvedPlanFilePath, planTitle, planSummary },
        };
      }

      return {
        content: [{
          type: "text",
          text: buildContinuePlanningResult(feedback),
        }],
        data: { plan, action, ...(feedback ? { feedback } : {}) },
      };
    },
  };
}