import { getFilesInput, getTextInput } from "../../core/io/input-types";
import type { ToolInput } from "../../core/io/input-types";
import type { ToolResult } from "../../core/io/output-types";
import type { ToolContext } from "../../core/plugins/plugin-types";
import type { FileChecksumVerifierOptions } from "./index";

function formatSize(bytes: number): string {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}

export async function runFileChecksumVerifier(
  input: ToolInput,
  options: FileChecksumVerifierOptions,
  context: ToolContext
): Promise<ToolResult> {
  const files = getFilesInput(input, "files");
  const expectedChecksum = getTextInput(input, "expectedChecksum").trim().toLowerCase();

  if (!files || files.length === 0) {
    throw new Error("Please select at least one file to verify.");
  }

  const { algorithm } = options;
  const rows: Array<[string, string, string, string, string]> = [];

  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const fileNum = i + 1;
    const totalFiles = files.length;

    context.reportProgress({
      percentage: Math.round(((i) / totalFiles) * 100),
      message: `Reading ${file.name} (${fileNum}/${totalFiles})...`,
    });

    let arrayBuffer: ArrayBuffer;
    try {
      arrayBuffer = await file.arrayBuffer();
    } catch (err: any) {
      throw new Error(`Failed to read file "${file.name}": ${err.message ?? err}`);
    }

    context.reportProgress({
      percentage: Math.round(((i + 0.5) / totalFiles) * 100),
      message: `Computing ${algorithm} for ${file.name}...`,
    });

    let hashHex = "";
    try {
      const hashBuffer = await crypto.subtle.digest(algorithm, arrayBuffer);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      hashHex = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
    } catch (err: any) {
      throw new Error(`Failed to compute hash for file "${file.name}": ${err.message ?? err}`);
    }

    let matchStatus = "N/A";
    if (expectedChecksum) {
      matchStatus = hashHex === expectedChecksum ? "✅ Match" : "❌ Mismatch";
    }

    rows.push([
      file.name,
      formatSize(file.size),
      hashHex,
      expectedChecksum || "(none)",
      matchStatus,
    ]);
  }

  context.reportProgress({ percentage: 100, message: "Done" });

  return {
    type: "table",
    columns: ["File Name", "Size", "Computed Hash", "Expected Hash", "Status"],
    rows,
  };
}
