import { Snowyflake, Epoch } from "snowyflake";
import { MJInfo, MJOptions, MJSubscribe } from "../interfaces";

export const sleep = async (ms: number): Promise<void> =>
  await new Promise((resolve) => setTimeout(resolve, ms));

export const random = (min: number, max: number): number =>
  Math.floor(Math.random() * (max - min) + min);

const snowflake = new Snowyflake({
  workerId: 0n,
  processId: 0n,
  epoch: Epoch.Discord, // BigInt timestamp
});

export const nextNonce = (): string => snowflake.nextId().toString();

export const formatPrompts = (prompts: string) => {
  const regex = /(\d️⃣ .+)/g;
  const matches = prompts.match(regex);
  if (matches) {
    const shortenedPrompts = matches.map((match) => match.trim());
    return shortenedPrompts;
  } else {
    return [];
  }
};

export const formatOptions = (components: any) => {
  var data: MJOptions[] = [];
  for (var i = 0; i < components.length; i++) {
    const component = components[i];
    if (component.components && component.components.length > 0) {
      const item = formatOptions(component.components);
      data = data.concat(item);
    }
    if (!component.custom_id) continue;
    data.push({
      type: component.type,
      style: component.style,
      label: component.label || component.emoji?.name,
      custom: component.custom_id,
    });
  }
  return data;
};

export const componentsToHash = (components: any) => {
  const options = formatOptions(components);
  for (let i = 0; i < options.length; i++) {
    const option = options[i];
    const list = option.custom.split('::');
    const hash = list.find(item => item.length === 36 && /^\w+(-\w+)+$/.test(item));
    if (hash) {
      return hash;
    }
  }
}

export const formatInfo = (msg: string) => {
  let jsonResult: MJInfo = {
    subscription: "",
    jobMode: "",
    visibilityMode: "",
    fastTimeRemaining: "",
    lifetimeUsage: "",
    relaxedUsage: "",
    queuedJobsFast: "",
    queuedJobsRelax: "",
    runningJobs: "",
    message: "",
  }; // Initialize jsonResult with empty object

  msg.split("\n").forEach(function (line) {
    const colonIndex = line.indexOf(":");
    if (colonIndex > -1) {
      const key = line.substring(0, colonIndex).trim().replaceAll("**", "");
      const value = line.substring(colonIndex + 1).trim();
      switch (key) {
        case "Subscription":
          jsonResult.subscription = value;
          break;
        case "Job Mode":
          jsonResult.jobMode = value;
          break;
        case "Visibility Mode":
          jsonResult.visibilityMode = value;
          break;
        case "Fast Time Remaining":
          jsonResult.fastTimeRemaining = value;
          break;
        case "Lifetime Usage":
          jsonResult.lifetimeUsage = value;
          break;
        case "Relaxed Usage":
          jsonResult.relaxedUsage = value;
          break;
        case "Queued Jobs (fast)":
          jsonResult.queuedJobsFast = value;
          break;
        case "Queued Jobs (relax)":
          jsonResult.queuedJobsRelax = value;
          break;
        case "Running Jobs":
          jsonResult.runningJobs = value;
          break;
        default:
        // Do nothing
      }
    }
  });

  if (!jsonResult.subscription) {
    jsonResult.message = msg;
  }
  return jsonResult;
};

export const formatSubscribe = (msg: MJSubscribe) => {
  return msg;
};

// works for done png image
export const uriToHash = (uri: string) => {
  return uri.split("_").pop()?.split(".")[0] ?? "";
};

// works for progress webp image
export const filenameToHash = (filename: string) => {
  return filename.split("_")[0] ?? "";
};

export const content2progress = (content: string) => {
  if (!content) return "";
  const spcon = content.split("<@");
  if (spcon.length < 2) {
    return "";
  }
  content = spcon[1];
  const regex = /\(([^)]+)\)/; // matches the value inside the first parenthesis
  const match = content.match(regex);
  let progress = "";
  if (match) {
    if (match[1].includes('fast') || match[1].includes('relax') || match[1].includes('turbo')) {
      progress = 'done';
    } else {
      progress = match[1];
    }
  }
  return progress;
};

// 求最大公约数
const gcd = (a: number, b: number): number => b ? gcd(b, a % b) : a;

// parseAr('--ar 1080:960', '1080:960')
// '--ar 9:8'
const parseAr = (ar: string, arNumber: string) => {
  if (!ar) return '';
  const arr = arNumber.split(':');
  const a = Number(arr[0]);
  const b = Number(arr[1]);
  const gb = gcd(a, b);
  return `--ar ${a/gb}:${b/gb}`;
}

// mj will parse prompt like put url into shortlink <shorurl>, and transform ar
const parsePrompt = (prompt: string = '') => {
  return prompt.replace(/^<[^<]+>\s/, '')
    // --ar 40:20 will be transformed into --ar 2:1
    .replace(/--ar ([\S]+)/i, parseAr);
}

export const content2prompt = (content: string) => {
  if (!content) return "";
  const pattern = /\*\*(.*?)\*\*/; // Match **middle content
  const matches = content.match(pattern);
  if (matches && matches.length > 1 && matches[1]) {
    const prompt = matches[1]; // Get the matched content
    // url will be transformed into <short url>
    return parsePrompt(prompt);
  } else {
    console.log("No match found.", content);
    return parsePrompt(content);
  }
};

export function custom2Type(custom: string) {
  if (custom.includes("upsample")) {
    return "upscale";
  } else if (custom.includes("variation")) {
    const list = custom.split('::');
    return list[2] || "variation";
  } else if (custom.includes("reroll")) {
    return "reroll";
  } else if (custom.includes("CustomZoom")) {
    return "customZoom";
  } else if (custom.includes("Outpaint")) {
    return "outpaint";
  } else if (custom.includes("remaster")) {
    return "reroll";
  } else if (custom.includes("pan_")) {
    return "pan";
  } else {
    const list = custom.split('::');
    return list[2];
  }
}

export const toRemixCustom = (customID: string) => {
  const parts = customID.split("::");
  const convertedString = `MJ::RemixModal::${parts[4]}::${parts[3]}::0`;
  return convertedString;
};

export async function base64ToBlob(base64Image: string): Promise<Blob> {
  // 移除 base64 图像头部信息
  const base64Data = base64Image.replace(
    /^data:image\/(png|jpeg|jpg);base64,/,
    ""
  );

  // 将 base64 数据解码为二进制数据
  const binaryData = atob(base64Data);

  // 创建一个 Uint8Array 来存储二进制数据
  const arrayBuffer = new ArrayBuffer(binaryData.length);
  const uint8Array = new Uint8Array(arrayBuffer);
  for (let i = 0; i < binaryData.length; i++) {
    uint8Array[i] = binaryData.charCodeAt(i);
  }

  // 使用 Uint8Array 创建 Blob 对象
  return new Blob([uint8Array], { type: "image/png" }); // 替换为相应的 MIME 类型
}
