import { data } from "react-router";

import { isDataWithApiError } from "~/server/headscale/api/error-client";
import { Capabilities } from "~/server/web/roles";

import type { Route } from "./+types/overview";

// We only check capabilities here and assume it is writable
// If it isn't, it'll gracefully error anyways, since this means some
// fishy client manipulation is happening.
export async function aclAction({ request, context }: Route.ActionArgs) {
  const principal = await context.auth.require(request);
  const check = context.auth.can(principal, Capabilities.write_policy);
  if (!check) {
    throw data("You do not have permission to write to the ACL policy", {
      status: 403,
    });
  }

  // Try to write to the ACL policy via the API or via config file (TODO).
  const formData = await request.formData();
  const policyData = formData.get("policy")?.toString();
  if (!policyData) {
    throw data("Missing `policy` in the form data.", {
      status: 400,
    });
  }

  const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
  const api = context.hsApi.getRuntimeClient(apiKey);
  try {
    const { policy, updatedAt } = await api.setPolicy(policyData);
    return data({
      success: true,
      error: undefined,
      policy,
      updatedAt,
    });
  } catch (error) {
    if (isDataWithApiError(error)) {
      const rawData = error.data.rawData;
      // https://github.com/juanfont/headscale/blob/c4600346f9c29b514dc9725ac103efb9d0381f23/hscontrol/types/policy.go#L11
      if (rawData.includes("update is disabled")) {
        throw data("Policy is not writable", { status: 403 });
      }

      const message =
        error.data.data != null &&
        "message" in error.data.data &&
        typeof error.data.data.message === "string"
          ? error.data.data.message
          : undefined;

      if (message == null) {
        throw error;
      }

      // Starting in Headscale 0.27.0 the ACLs parsing was changed meaning
      // we need to reference other error messages based on API version.
      if (context.hsApi.clientHelpers.isAtleast("0.27.0")) {
        if (message.includes("parsing HuJSON:")) {
          const cutIndex = message.indexOf("parsing HuJSON:");
          const trimmed =
            cutIndex > -1 ? `Syntax error: ${message.slice(cutIndex + 16).trim()}` : message;

          return data(
            {
              success: false,
              error: trimmed,
              policy: undefined,
              updatedAt: undefined,
            },
            400,
          );
        }

        if (message.includes("parsing policy from bytes:")) {
          const cutIndex = message.indexOf("parsing policy from bytes:");
          const trimmed =
            cutIndex > -1 ? `Syntax error: ${message.slice(cutIndex + 26).trim()}` : message;

          return data(
            {
              success: false,
              error: trimmed,
              policy: undefined,
              updatedAt: undefined,
            },
            400,
          );
        }
      } else {
        // Pre-0.27.0 error messages
        if (message.includes("parsing hujson")) {
          const cutIndex = message.indexOf("err: hujson:");
          const trimmed = cutIndex > -1 ? `Syntax error: ${message.slice(cutIndex + 12)}` : message;

          return data(
            {
              success: false,
              error: trimmed,
              policy: undefined,
              updatedAt: undefined,
            },
            400,
          );
        }

        if (message.includes("unmarshalling policy")) {
          const cutIndex = message.indexOf("err:");
          const trimmed = cutIndex > -1 ? `Syntax error: ${message.slice(cutIndex + 5)}` : message;

          return data(
            {
              success: false,
              error: trimmed,
              policy: undefined,
              updatedAt: undefined,
            },
            400,
          );
        }
      }
    }

    // Otherwise, this is a Headscale error that we can just propagate.
    throw error;
  }
}
