import { BrowserWindow, clipboard } from "electron";
import { platform, arch } from "os";
import { readSettings } from "../../main/settings";
import { createTypedHandler } from "./base";
import { systemContracts } from "../types/system";
import { miscContracts } from "../types/misc";
import type { SystemDebugInfo } from "../types/system";

import log from "electron-log";
import path from "path";
import fs from "fs";
import { runShellCommand } from "../utils/runShellCommand";
import { extractCodebase } from "../../utils/codebase";
import { db } from "../../db";
import { chats, apps } from "../../db/schema";
import { eq } from "drizzle-orm";
import { getDyadAppPath } from "../../paths/paths";
import { LargeLanguageModel } from "@/lib/schemas";
import { validateChatContext } from "../utils/context_paths_utils";

// Shared function to get system debug info
async function getSystemDebugInfo({
  linesOfLogs,
  level,
}: {
  linesOfLogs: number;
  level: "warn" | "info";
}): Promise<SystemDebugInfo> {
  console.log("Getting system debug info");

  // Get Node.js and pnpm versions
  let nodeVersion: string | null = null;
  let pnpmVersion: string | null = null;
  let nodePath: string | null = null;
  try {
    nodeVersion = await runShellCommand("node --version");
  } catch (err) {
    console.error("Failed to get Node.js version:", err);
  }

  try {
    pnpmVersion = await runShellCommand("pnpm --version");
  } catch (err) {
    console.error("Failed to get pnpm version:", err);
  }

  try {
    if (platform() === "win32") {
      nodePath = await runShellCommand("where.exe node");
    } else {
      nodePath = await runShellCommand("which node");
    }
  } catch (err) {
    console.error("Failed to get node path:", err);
  }

  // Get Dyad version from package.json
  const packageJsonPath = path.resolve(__dirname, "..", "..", "package.json");
  let dyadVersion = "unknown";
  try {
    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
    dyadVersion = packageJson.version;
  } catch (err) {
    console.error("Failed to read package.json:", err);
  }

  // Get telemetry info from settings
  const settings = readSettings();
  const telemetryId = settings.telemetryUserId || "unknown";

  // Get logs from electron-log
  let logs = "";
  try {
    const logPath = log.transports.file.getFile().path;
    if (fs.existsSync(logPath)) {
      const logContent = fs.readFileSync(logPath, "utf8");

      const logLines = logContent.split("\n").filter((line) => {
        if (level === "info") {
          return true;
        }
        // Example line:
        // [2025-06-09 13:55:05.209] [debug] (runShellCommand) Command "which node" succeeded with code 0: /usr/local/bin/node
        const logLevelRegex = /\[.*?\] \[(\w+)\]/;
        const match = line.match(logLevelRegex);
        if (!match) {
          // Include non-matching lines (like stack traces) when filtering for warnings
          return true;
        }
        const logLevel = match[1];
        if (level === "warn") {
          return logLevel === "warn" || logLevel === "error";
        }
        return true;
      });

      logs = logLines.slice(-linesOfLogs).join("\n");
    }
  } catch (err) {
    console.error("Failed to read log file:", err);
    logs = `Error reading logs: ${err}`;
  }

  return {
    nodeVersion,
    pnpmVersion,
    nodePath,
    telemetryId,
    selectedLanguageModel:
      serializeModelForDebug(settings.selectedModel) || "unknown",
    telemetryConsent: settings.telemetryConsent || "unknown",
    telemetryUrl: "https://us.i.posthog.com", // Hardcoded from renderer.tsx
    dyadVersion,
    platform: process.platform,
    architecture: arch(),
    logs,
  };
}

export function registerDebugHandlers() {
  createTypedHandler(systemContracts.getSystemDebugInfo, async () => {
    console.log("IPC: get-system-debug-info called");
    return getSystemDebugInfo({
      linesOfLogs: 20,
      level: "warn",
    });
  });

  createTypedHandler(miscContracts.getChatLogs, async (_, chatId) => {
    console.log(`IPC: get-chat-logs called for chat ${chatId}`);

    try {
      // We can retrieve a lot more lines here because we're not limited by the
      // GitHub issue URL length limit.
      const debugInfo = await getSystemDebugInfo({
        linesOfLogs: 1_000,
        level: "info",
      });

      // Get chat data from database
      const chatRecord = await db.query.chats.findFirst({
        where: eq(chats.id, chatId),
        with: {
          messages: {
            orderBy: (messages, { asc }) => [asc(messages.createdAt)],
          },
        },
      });

      if (!chatRecord) {
        throw new Error(`Chat with ID ${chatId} not found`);
      }

      // Format the chat to match the Chat interface
      const chat = {
        id: chatRecord.id,
        title: chatRecord.title || "Untitled Chat",
        messages: chatRecord.messages.map((msg) => ({
          id: msg.id,
          role: msg.role,
          content: msg.content,
          approvalState: msg.approvalState,
        })),
      };

      // Get app data from database
      const app = await db.query.apps.findFirst({
        where: eq(apps.id, chatRecord.appId),
      });

      if (!app) {
        throw new Error(`App with ID ${chatRecord.appId} not found`);
      }

      // Extract codebase
      const appPath = getDyadAppPath(app.path);
      const codebase = (
        await extractCodebase({
          appPath,
          chatContext: validateChatContext(app.chatContext),
        })
      ).formattedOutput;

      return {
        debugInfo,
        chat,
        codebase,
      };
    } catch (error) {
      console.error(`Error in get-chat-logs:`, error);
      throw error;
    }
  });

  console.log("Registered debug IPC handlers");

  createTypedHandler(systemContracts.takeScreenshot, async () => {
    const win = BrowserWindow.getFocusedWindow();
    if (!win) throw new Error("No focused window to capture");

    // Capture the window's current contents as a NativeImage
    const image = await win.capturePage();
    // Validate image
    if (!image || image.isEmpty()) {
      throw new Error("Failed to capture screenshot");
    }
    // Write the image to the clipboard
    clipboard.writeImage(image);
  });
}

function serializeModelForDebug(model: LargeLanguageModel): string {
  return `${model.provider}:${model.name} | customId: ${model.customModelId}`;
}
