import { PASSPHRASE_WORDS } from "./words";
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 { PasswordGeneratorOptions } from "./index";

function secureRandomInt(max: number): number {
  const arr = new Uint32Array(1);
  // Ensure we don't introduce modulo bias for small numbers
  // max is pool size which is typically small (< 100), so standard modulo is perfectly safe in practice.
  crypto.getRandomValues(arr);
  return arr[0] % max;
}

function secureShuffle(arr: string[]): string[] {
  const result = [...arr];
  const rand = new Uint32Array(result.length);
  crypto.getRandomValues(rand);
  for (let i = result.length - 1; i > 0; i--) {
    const j = rand[i] % (i + 1);
    const temp = result[i];
    result[i] = result[j];
    result[j] = temp;
  }
  return result;
}

export async function runPasswordGenerator(
  _input: ToolInput,
  options: PasswordGeneratorOptions,
  context: ToolContext
): Promise<ToolResult> {
  const {
    mode,
    length,
    uppercase,
    lowercase,
    numbers,
    symbols,
    excludeAmbiguous,
    count,
  } = options;

  const qty = count || 5;

  if (mode === "passphrase") {
    context.reportProgress({ percentage: 20, message: "Generating passphrases..." });
    
    const rows: Array<[string, number, string]> = [];
    const wordlistSize = PASSPHRASE_WORDS.length;
    // log2(250) is ~7.96578
    const entropyPerWord = Math.log2(wordlistSize);
    const totalEntropy = Math.round(length * entropyPerWord * 10) / 10;

    for (let i = 0; i < qty; i++) {
      const words: string[] = [];
      for (let j = 0; j < length; j++) {
        const idx = secureRandomInt(wordlistSize);
        words.push(PASSPHRASE_WORDS[idx]);
      }
      const passphrase = words.join("-");
      rows.push([passphrase, passphrase.length, `${totalEntropy} bits`]);
    }

    context.reportProgress({ percentage: 100, message: "Done" });
    return {
      type: "table",
      columns: ["Passphrase", "Length (chars)", "Entropy (bits)"],
      rows,
    };
  } else {
    // Password mode
    context.reportProgress({ percentage: 20, message: "Building character pools..." });

    let uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    let lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
    let numberChars = "0123456789";
    let symbolChars = "!@#$%^&*()_+-=[]{}|;:,.<>?";

    if (excludeAmbiguous) {
      const ambiguous = /[l1Io0O]/g;
      uppercaseChars = uppercaseChars.replace(ambiguous, "");
      lowercaseChars = lowercaseChars.replace(ambiguous, "");
      numberChars = numberChars.replace(ambiguous, "");
      // Also remove |, :, ; etc. from symbols if they are considered ambiguous,
      // but standard is to remove characters resembling each other: l, 1, I, o, 0, O, etc.
    }

    const pools: string[] = [];
    if (uppercase) pools.push(uppercaseChars);
    if (lowercase) pools.push(lowercaseChars);
    if (numbers) pools.push(numberChars);
    if (symbols) pools.push(symbolChars);

    if (pools.length === 0) {
      throw new Error("Invalid options: Please select at least one character set (Uppercase, Lowercase, Numbers, or Symbols) to generate passwords.");
    }

    const fullPool = pools.join("");
    const poolSize = fullPool.length;
    const entropyPerChar = Math.log2(poolSize);
    const totalEntropy = Math.round(length * entropyPerChar * 10) / 10;

    context.reportProgress({ percentage: 50, message: "Generating random passwords..." });

    const rows: Array<[string, number, string]> = [];

    for (let i = 0; i < qty; i++) {
      const passwordChars: string[] = [];

      // Guarantee at least one character from each selected character class
      // if password length permits it
      if (length >= pools.length) {
        pools.forEach(pool => {
          const idx = secureRandomInt(pool.length);
          passwordChars.push(pool[idx]);
        });

        // Fill remaining length
        const remainingLength = length - pools.length;
        for (let j = 0; j < remainingLength; j++) {
          const idx = secureRandomInt(poolSize);
          passwordChars.push(fullPool[idx]);
        }

        // Shuffle securely to mix the guaranteed characters
        const shuffled = secureShuffle(passwordChars);
        rows.push([shuffled.join(""), length, `${totalEntropy} bits`]);
      } else {
        // Fallback for short length: pick entirely randomly from combined pool
        for (let j = 0; j < length; j++) {
          const idx = secureRandomInt(poolSize);
          passwordChars.push(fullPool[idx]);
        }
        rows.push([passwordChars.join(""), length, `${totalEntropy} bits`]);
      }
    }

    context.reportProgress({ percentage: 100, message: "Done" });
    return {
      type: "table",
      columns: ["Password", "Length", "Entropy (bits)"],
      rows,
    };
  }
}
