import {
  createSkill,
  createSkillVersion,
  deleteSkill,
  deleteSkillVersion,
  getSkill,
  getSkillVersion,
  listSkillVersions,
  listSkills
} from "./chunk-3YNXL3XA.js";
import {
  effectiveTenant,
  resolveCreateTenant
} from "./chunk-K7DLILHK.js";
import {
  decodeCursor,
  jsonOk,
  paginatedOk,
  routeWrap
} from "./chunk-QSHK4MUE.js";
import {
  badRequest,
  notFound
} from "./chunk-EZYKRG4W.js";

// src/handlers/skills-write.ts
import { z } from "zod";
import { inflateRawSync } from "zlib";
function extractFromZipRaw(buffer) {
  const files = /* @__PURE__ */ new Map();
  let offset = 0;
  while (offset + 30 <= buffer.length) {
    const sig = buffer.readUInt32LE(offset);
    if (sig !== 67324752) break;
    const compMethod = buffer.readUInt16LE(offset + 8);
    const compSize = buffer.readUInt32LE(offset + 18);
    const uncompSize = buffer.readUInt32LE(offset + 22);
    const nameLen = buffer.readUInt16LE(offset + 26);
    const extraLen = buffer.readUInt16LE(offset + 28);
    const name = buffer.toString("utf8", offset + 30, offset + 30 + nameLen);
    const dataStart = offset + 30 + nameLen + extraLen;
    if (compMethod === 0) {
      files.set(name, buffer.subarray(dataStart, dataStart + uncompSize));
    } else if (compMethod === 8) {
      const compressed = buffer.subarray(dataStart, dataStart + compSize);
      files.set(name, inflateRawSync(compressed));
    }
    offset = dataStart + compSize;
  }
  return files;
}
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
  ".md",
  ".txt",
  ".py",
  ".js",
  ".ts",
  ".tsx",
  ".jsx",
  ".sh",
  ".bash",
  ".json",
  ".yaml",
  ".yml",
  ".html",
  ".css",
  ".xml",
  ".csv",
  ".toml",
  ".cfg",
  ".ini",
  ".sql",
  ".rb",
  ".go",
  ".rs",
  ".c",
  ".h",
  ".cpp",
  ".java",
  ".kt",
  ".swift",
  ".r",
  ".pl",
  ".lua",
  ".env",
  ".gitignore",
  ".dockerfile",
  ".makefile",
  ".cmake",
  ".conf",
  ".properties",
  ".lock"
]);
function isTextFile(name) {
  if (name.endsWith("/")) return false;
  const dotIdx = name.lastIndexOf(".");
  if (dotIdx === -1) return true;
  return TEXT_EXTENSIONS.has(name.substring(dotIdx).toLowerCase());
}
function convertZipEntries(raw) {
  const files = /* @__PURE__ */ new Map();
  for (const [name, buf] of raw) {
    if (isTextFile(name)) {
      files.set(name, buf.toString("utf-8"));
    } else {
      files.set(name, "base64:" + buf.toString("base64"));
    }
  }
  return files;
}
var CreateSkillSchema = z.object({
  name: z.string().min(1).max(256),
  description: z.string().max(2048).optional(),
  content: z.string().min(1).max(256 * 1024),
  // 256 KB
  tenant_id: z.string().optional()
});
var CreateVersionSchema = z.object({
  content: z.string().min(1).max(256 * 1024),
  version: z.string().min(1).max(64).optional()
});
function handleCreateSkill(request) {
  return routeWrap(request, async ({ auth }) => {
    const contentType = request.headers.get("content-type") ?? "";
    let name;
    let description;
    let content;
    let filesMap;
    let tenantId;
    if (contentType.includes("multipart/form-data")) {
      const formData = await request.formData();
      name = (formData.get("display_title") ?? "").trim() || "untitled";
      const file = formData.get("files[]") ?? formData.get("files") ?? formData.get("file");
      if (!file || !(file instanceof File)) {
        throw badRequest("Missing file in multipart upload");
      }
      const buffer = Buffer.from(await file.arrayBuffer());
      if (file.name?.toLowerCase().endsWith(".zip")) {
        const rawEntries = extractFromZipRaw(buffer);
        const zipFiles = convertZipEntries(rawEntries);
        const skillEntry = [...zipFiles.entries()].find(
          ([k]) => k.endsWith("SKILL.md") || k.endsWith("skill.md")
        );
        if (!skillEntry) {
          throw badRequest("No SKILL.md found in zip archive");
        }
        content = skillEntry[1];
        const builtFiles = {};
        const prefix = skillEntry[0].substring(0, skillEntry[0].lastIndexOf("/") + 1);
        for (const [path, fileContent] of zipFiles.entries()) {
          if (path.endsWith("/")) continue;
          const relativePath = path.startsWith(prefix) ? path.slice(prefix.length) : path;
          builtFiles[relativePath] = fileContent;
        }
        if (Object.keys(builtFiles).length > 1 || !builtFiles["SKILL.md"]) {
          filesMap = builtFiles;
        }
      } else {
        content = buffer.toString("utf-8");
      }
      tenantId = resolveCreateTenant(auth, void 0);
    } else {
      const body = await request.json().catch(() => null);
      const parsed = CreateSkillSchema.safeParse(body);
      if (!parsed.success) {
        throw badRequest(
          `invalid body: ${parsed.error.issues.map((i) => i.message).join("; ")}`
        );
      }
      name = parsed.data.name;
      description = parsed.data.description;
      content = parsed.data.content;
      tenantId = resolveCreateTenant(auth, parsed.data.tenant_id);
    }
    const skill = createSkill({ name, description, content, files: filesMap, tenantId });
    return jsonOk(skill, 201);
  });
}
function handleListSkills(request) {
  return routeWrap(request, async ({ auth, request: req }) => {
    const url = new URL(req.url);
    const limit = Math.min(
      Math.max(Number(url.searchParams.get("limit") || "20"), 1),
      100
    );
    const cursor = decodeCursor(url.searchParams.get("after_id"));
    const includeArchived = url.searchParams.get("include_archived") === "true";
    const tenantId = effectiveTenant(auth) ?? void 0;
    const skills = listSkills({
      limit,
      cursor: cursor ?? void 0,
      tenantId,
      includeArchived
    });
    return paginatedOk(skills, limit);
  });
}
function handleGetSkill(request, skillId) {
  return routeWrap(request, async () => {
    const skill = getSkill(skillId);
    if (!skill) throw notFound(`skill ${skillId} not found`);
    return jsonOk(skill);
  });
}
function handleDeleteSkill(request, skillId) {
  return routeWrap(request, async () => {
    const ok = deleteSkill(skillId);
    if (!ok) throw notFound(`skill ${skillId} not found`);
    return jsonOk({ id: skillId, type: "skill_deleted" });
  });
}
function handleCreateSkillVersion(request, skillId) {
  return routeWrap(request, async () => {
    const body = await request.json().catch(() => null);
    const parsed = CreateVersionSchema.safeParse(body);
    if (!parsed.success) {
      throw badRequest(
        `invalid body: ${parsed.error.issues.map((i) => i.message).join("; ")}`
      );
    }
    const sv = createSkillVersion(skillId, {
      content: parsed.data.content,
      version: parsed.data.version
    });
    if (!sv) throw notFound(`skill ${skillId} not found`);
    return jsonOk(sv, 201);
  });
}
function handleListSkillVersions(request, skillId) {
  return routeWrap(request, async ({ request: req }) => {
    const skill = getSkill(skillId);
    if (!skill) throw notFound(`skill ${skillId} not found`);
    const url = new URL(req.url);
    const limit = Math.min(
      Math.max(Number(url.searchParams.get("limit") || "20"), 1),
      100
    );
    const cursor = decodeCursor(url.searchParams.get("after_id"));
    const versions = listSkillVersions(skillId, { limit, cursor });
    return paginatedOk(versions, limit);
  });
}
function handleGetSkillVersion(request, skillId, version) {
  return routeWrap(request, async () => {
    const sv = getSkillVersion(skillId, version);
    if (!sv) throw notFound(`skill version ${version} not found`);
    return jsonOk(sv);
  });
}
function handleGetSkillVersionContent(request, skillId, version) {
  return routeWrap(request, async () => {
    const skill = getSkill(skillId);
    if (!skill) throw notFound("skill not found");
    const sv = getSkillVersion(skillId, version);
    if (!sv) throw notFound("skill version not found");
    return new Response(sv.content, {
      headers: {
        "Content-Type": "text/markdown",
        "Content-Disposition": `attachment; filename="${skill.name}-${version}.md"`,
        "Content-Length": String(Buffer.byteLength(sv.content, "utf-8"))
      }
    });
  });
}
function handleDeleteSkillVersion(request, skillId, version) {
  return routeWrap(request, async () => {
    const result = deleteSkillVersion(skillId, version);
    if (!result.ok) {
      if (result.reason === "cannot delete the current version") {
        throw badRequest(result.reason);
      }
      throw notFound(`skill version ${version} not found`);
    }
    return jsonOk({ skill_id: skillId, version, type: "skill_version_deleted" });
  });
}

export {
  handleCreateSkill,
  handleListSkills,
  handleGetSkill,
  handleDeleteSkill,
  handleCreateSkillVersion,
  handleListSkillVersions,
  handleGetSkillVersion,
  handleGetSkillVersionContent,
  handleDeleteSkillVersion
};
