import * as fs from "fs/promises";
import * as fsSync from "fs";
import path from "path";
import { ArtifactRepository, TaskEventRepository, TaskRepository, WorkspaceRepository } from "../../database/repositories";
import type { MCPResource, MCPResourceReadResult, MCPTool } from "../types";

type ToolDelegate = {
  getTools(): MCPTool[];
  executeTool(name: string, args: Record<string, Any>): Promise<Any>;
};

function toJsonResource(uri: string, value: unknown): MCPResourceReadResult {
  return {
    contents: [
      {
        uri,
        mimeType: "application/json",
        text: JSON.stringify(value, null, 2),
      },
    ],
  };
}

function isTextLikeMimeType(mimeType: string): boolean {
  return (
    mimeType.startsWith("text/") ||
    mimeType === "application/json" ||
    mimeType === "application/xml" ||
    mimeType.includes("javascript") ||
    mimeType.includes("typescript")
  );
}

export class CoWorkHostProvider {
  constructor(
    private deps: {
      workspaceRepo: WorkspaceRepository;
      taskRepo: TaskRepository;
      taskEventRepo: TaskEventRepository;
      artifactRepo: ArtifactRepository;
      toolDelegate: ToolDelegate;
    },
  ) {}

  getTools(): MCPTool[] {
    return this.deps.toolDelegate.getTools();
  }

  async executeTool(name: string, args: Record<string, Any>): Promise<Any> {
    return this.deps.toolDelegate.executeTool(name, args);
  }

  getResources(): MCPResource[] {
    const resources: MCPResource[] = [
      {
        uri: "cowork://workspaces",
        name: "Workspaces",
        description: "List all local workspaces.",
        mimeType: "application/json",
      },
      {
        uri: "cowork://tasks",
        name: "Recent Tasks",
        description: "List recent local tasks.",
        mimeType: "application/json",
      },
    ];

    const workspaces = this.deps.workspaceRepo.findAll().slice(0, 25);
    for (const workspace of workspaces) {
      resources.push({
        uri: `cowork://workspaces/${workspace.id}`,
        name: workspace.name,
        description: `Workspace ${workspace.name}`,
        mimeType: "application/json",
      });
    }

    const tasks = this.deps.taskRepo.findAll(25, 0);
    for (const task of tasks) {
      resources.push(
        {
          uri: `cowork://tasks/${task.id}`,
          name: task.title,
          description: `Task ${task.status}`,
          mimeType: "application/json",
        },
        {
          uri: `cowork://tasks/${task.id}/events`,
          name: `${task.title} events`,
          description: "Ordered task event log",
          mimeType: "application/json",
        },
        {
          uri: `cowork://tasks/${task.id}/artifacts`,
          name: `${task.title} artifacts`,
          description: "Artifacts generated by the task",
          mimeType: "application/json",
        },
      );
      for (const artifact of this.deps.artifactRepo.findByTaskId(task.id).slice(0, 10)) {
        resources.push({
          uri: `cowork://artifacts/${artifact.id}`,
          name: path.basename(artifact.path),
          description: `Artifact for task ${task.title}`,
          mimeType: artifact.mimeType || "application/octet-stream",
        });
      }
    }

    return resources;
  }

  async readResource(uri: string): Promise<MCPResourceReadResult> {
    const parsed = new URL(uri);
    const pathSegments = parsed.pathname.split("/").filter(Boolean);

    if (parsed.hostname === "workspaces" && pathSegments.length === 0) {
      return toJsonResource(uri, this.deps.workspaceRepo.findAll());
    }
    if (parsed.hostname === "workspaces" && pathSegments.length === 1) {
      const workspace = this.deps.workspaceRepo.findById(pathSegments[0]);
      if (!workspace) throw new Error("Workspace not found");
      return toJsonResource(uri, workspace);
    }

    if (parsed.hostname === "tasks" && pathSegments.length === 0) {
      return toJsonResource(uri, this.deps.taskRepo.findAll(100, 0));
    }
    if (parsed.hostname === "tasks" && pathSegments.length === 1) {
      const task = this.deps.taskRepo.findById(pathSegments[0]);
      if (!task) throw new Error("Task not found");
      return toJsonResource(uri, task);
    }
    if (parsed.hostname === "tasks" && pathSegments.length === 2 && pathSegments[1] === "events") {
      return toJsonResource(uri, this.deps.taskEventRepo.findByTaskId(pathSegments[0]));
    }
    if (parsed.hostname === "tasks" && pathSegments.length === 2 && pathSegments[1] === "artifacts") {
      return toJsonResource(uri, this.deps.artifactRepo.findByTaskId(pathSegments[0]));
    }

    if (parsed.hostname === "artifacts" && pathSegments.length === 1) {
      const artifactId = pathSegments[0];
      const artifact = this.deps.artifactRepo.findById(artifactId);
      if (!artifact) throw new Error("Artifact not found");

      if (!fsSync.existsSync(artifact.path)) {
        return toJsonResource(uri, {
          ...artifact,
          exists: false,
        });
      }

      if (isTextLikeMimeType(artifact.mimeType)) {
        const text = await fs.readFile(artifact.path, "utf8");
        return {
          contents: [
            {
              uri,
              mimeType: artifact.mimeType,
              text,
            },
          ],
        };
      }

      const data = await fs.readFile(artifact.path);
      return {
        contents: [
          {
            uri,
            mimeType: artifact.mimeType || "application/octet-stream",
            blob: data.toString("base64"),
          },
        ],
      };
    }

    throw new Error(`Unsupported resource uri: ${uri}`);
  }
}
