#!/usr/bin/env node

const { execSync } = require("node:child_process");
const {
  existsSync,
  mkdirSync,
  writeFileSync,
  readFileSync,
} = require("node:fs");
const path = require("node:path");

const { argv } = require("node:process");
const inputIdx = argv.indexOf("--input");
const INPUT_FILE = inputIdx > -1 ? argv[inputIdx + 1] : null;
const POSTPROCESS_ONLY = !!INPUT_FILE;

// __dirname is available in CommonJS by default

/**
 * Generate 3rd party licenses for frontend dependencies
 * This script creates a JSON file similar to the Java backend's 3rdPartyLicenses.json
 */

const OUTPUT_FILE = path.join(
  __dirname,
  "..",
  "src",
  "assets",
  "3rdPartyLicenses.json",
);
// package.json lives at the workspace root (frontend/), not editor/. The
// script is at frontend/editor/scripts/, so walk up two levels.
const PACKAGE_JSON = path.join(__dirname, "..", "..", "package.json");

// Ensure the output directory exists
const outputDir = path.dirname(OUTPUT_FILE);
if (!existsSync(outputDir)) {
  mkdirSync(outputDir, { recursive: true });
}

console.log("🔍 Generating frontend license report...");

try {
  // Safety guard: don't run this script on fork PRs (workflow setzt PR_IS_FORK)
  if (process.env.PR_IS_FORK === "true" && !POSTPROCESS_ONLY) {
    console.error(
      "Fork PR detected: only --input (postprocess-only) mode is allowed.",
    );
    process.exit(2);
  }

  let licenseData;
  // Generate license report using pinned license-checker; disable lifecycle scripts
  if (POSTPROCESS_ONLY) {
    if (!INPUT_FILE || !existsSync(INPUT_FILE)) {
      console.error("❌ --input file missing or not found");
      process.exit(1);
    }
    licenseData = JSON.parse(readFileSync(INPUT_FILE, "utf8"));
  } else {
    const licenseReport = execSync(
      // 'npx --yes license-checker@25.0.1 --production --json',
      "npx --yes license-report --only=prod --output=json",
      {
        encoding: "utf8",
        cwd: path.dirname(PACKAGE_JSON),
        env: { ...process.env, NPM_CONFIG_IGNORE_SCRIPTS: "true" },
      },
    );
    try {
      licenseData = JSON.parse(licenseReport);
    } catch (parseError) {
      console.error("❌ Failed to parse license data:", parseError.message);
      console.error("Raw output:", licenseReport.substring(0, 500) + "...");
      process.exit(1);
    }
  }

  if (!Array.isArray(licenseData)) {
    console.error("❌ Invalid license data structure");
    process.exit(1);
  }

  // Convert license-checker format to array
  const licenseArray = licenseData.map((dep) => {
    let licenseType = dep.licenseType;

    // Handle missing or null licenses
    if (!licenseType || licenseType === null || licenseType === undefined) {
      licenseType = "Unknown";
    }

    // Handle empty string licenses
    if (licenseType === "") {
      licenseType = "Unknown";
    }

    // Handle array licenses (rare but possible)
    if (Array.isArray(licenseType)) {
      licenseType = licenseType.join(" AND ");
    }

    // Handle object licenses (fallback)
    if (typeof licenseType === "object" && licenseType !== null) {
      licenseType = "Unknown";
    }

    if (
      "posthog-js" === dep.name &&
      licenseType.startsWith("SEE LICENSE IN LICENSE")
    ) {
      licenseType =
        "SEE LICENSE IN LICENSE https://github.com/PostHog/posthog-js/blob/main/LICENSE";
    }

    return {
      name: dep.name,
      version:
        dep.installedVersion ||
        dep.definedVersion ||
        dep.remoteVersion ||
        "unknown",
      licenseType: licenseType,
      repository: dep.link,
      url: dep.link,
      link: dep.link,
    };
  });

  // Transform to match Java backend format
  const transformedData = {
    dependencies: licenseArray.map((dep) => {
      const licenseType = Array.isArray(dep.licenseType)
        ? dep.licenseType.join(", ")
        : dep.licenseType || "Unknown";
      const licenseUrl = dep.link || getLicenseUrl(licenseType);

      return {
        moduleName: dep.name,
        moduleUrl:
          dep.repository ||
          dep.url ||
          `https://www.npmjs.com/package/${dep.name}`,
        moduleVersion: dep.version,
        moduleLicense: licenseType,
        moduleLicenseUrl: licenseUrl,
      };
    }),
  };

  // Log summary of license types found
  const licenseSummary = licenseArray.reduce((acc, dep) => {
    const license = Array.isArray(dep.licenseType)
      ? dep.licenseType.join(", ")
      : dep.licenseType || "Unknown";
    acc[license] = (acc[license] || 0) + 1;
    return acc;
  }, {});

  console.log("📊 License types found:");
  Object.entries(licenseSummary).forEach(([license, count]) => {
    console.log(`   ${license}: ${count} packages`);
  });

  // Log any complex or unusual license formats for debugging
  const complexLicenses = licenseArray.filter(
    (dep) =>
      dep.licenseType &&
      (dep.licenseType.includes("AND") ||
        dep.licenseType.includes("OR") ||
        dep.licenseType === "Unknown" ||
        dep.licenseType.includes("SEE LICENSE")),
  );

  if (complexLicenses.length > 0) {
    console.log("\n🔍 Complex/Edge case licenses detected:");
    complexLicenses.forEach((dep) => {
      console.log(`   ${dep.name}@${dep.version}: "${dep.licenseType}"`);
    });
  }

  // Check for potentially problematic licenses
  const problematicLicenses = checkLicenseCompatibility(
    licenseSummary,
    licenseArray,
  );
  if (problematicLicenses.length > 0) {
    console.log("\n⚠️  License compatibility warnings:");
    problematicLicenses.forEach((warning) => {
      console.log(`   ${warning.message}`);
    });

    // Write license warnings to a separate file for CI/CD
    const warningsFile = path.join(
      __dirname,
      "..",
      "src",
      "assets",
      "license-warnings.json",
    );
    writeFileSync(
      warningsFile,
      JSON.stringify(
        {
          warnings: problematicLicenses,
          generated: new Date().toISOString(),
        },
        null,
        2,
      ),
    );
    console.log(`⚠️  License warnings saved to: ${warningsFile}`);
  } else {
    console.log("\n✅ All licenses appear to be corporate-friendly");
  }

  // Write to file
  writeFileSync(OUTPUT_FILE, JSON.stringify(transformedData, null, 2) + "\n");

  console.log(`✅ License report generated successfully!`);
  console.log(`📄 Found ${transformedData.dependencies.length} dependencies`);
  console.log(`💾 Saved to: ${OUTPUT_FILE}`);
} catch (error) {
  console.error("❌ Error generating license report:", error.message);
  process.exit(1);
}

/**
 * Get standard license URLs for common licenses
 */
function getLicenseUrl(licenseType) {
  if (!licenseType || licenseType === "Unknown") return "";

  const licenseUrls = {
    MIT: "https://opensource.org/licenses/MIT",
    "MIT*": "https://opensource.org/licenses/MIT",
    "Apache-2.0": "https://www.apache.org/licenses/LICENSE-2.0",
    "Apache License 2.0": "https://www.apache.org/licenses/LICENSE-2.0",
    "BSD-3-Clause": "https://opensource.org/licenses/BSD-3-Clause",
    "BSD-2-Clause": "https://opensource.org/licenses/BSD-2-Clause",
    BSD: "https://opensource.org/licenses/BSD-3-Clause",
    "GPL-3.0": "https://www.gnu.org/licenses/gpl-3.0.html",
    "GPL-2.0": "https://www.gnu.org/licenses/gpl-2.0.html",
    "LGPL-2.1": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html",
    "LGPL-3.0": "https://www.gnu.org/licenses/lgpl-3.0.html",
    ISC: "https://opensource.org/licenses/ISC",
    "CC0-1.0": "https://creativecommons.org/publicdomain/zero/1.0/",
    Unlicense: "https://unlicense.org/",
    "MPL-2.0": "https://www.mozilla.org/en-US/MPL/2.0/",
    WTFPL: "http://www.wtfpl.net/",
    Zlib: "https://opensource.org/licenses/Zlib",
    "Artistic-2.0": "https://opensource.org/licenses/Artistic-2.0",
    "EPL-1.0": "https://www.eclipse.org/legal/epl-v10.html",
    "EPL-2.0": "https://www.eclipse.org/legal/epl-2.0/",
    "CDDL-1.0": "https://opensource.org/licenses/CDDL-1.0",
    Ruby: "https://www.ruby-lang.org/en/about/license.txt",
    "Python-2.0": "https://www.python.org/download/releases/2.0/license/",
    "Public Domain": "https://creativecommons.org/publicdomain/zero/1.0/",
    UNLICENSED: "",
  };

  // Try exact match first
  if (licenseUrls[licenseType]) {
    return licenseUrls[licenseType];
  }

  // Try case-insensitive match
  const lowerType = licenseType.toLowerCase();
  for (const [key, url] of Object.entries(licenseUrls)) {
    if (key.toLowerCase() === lowerType) {
      return url;
    }
  }

  // Handle complex SPDX expressions like "(MIT AND Zlib)" or "(MIT OR CC0-1.0)"
  if (licenseType.includes("AND") || licenseType.includes("OR")) {
    // Extract the first license from compound expressions for URL
    const match = licenseType.match(/\(?\s*([A-Za-z0-9\-.]+)/);
    if (match && licenseUrls[match[1]]) {
      return licenseUrls[match[1]];
    }
  }

  // For non-standard licenses, return empty string (will use package link if available)
  return "";
}

/**
 * Check for potentially problematic licenses that may not be MIT/corporate compatible
 */
function checkLicenseCompatibility(licenseSummary, licenseArray) {
  const warnings = [];

  // Define problematic license patterns
  const problematicLicenses = {
    // Copyleft licenses
    "GPL-2.0": "Strong copyleft license - requires derivative works to be GPL",
    "GPL-3.0": "Strong copyleft license - requires derivative works to be GPL",
    "LGPL-2.1":
      "Weak copyleft license - may require source disclosure for modifications",
    "LGPL-3.0":
      "Weak copyleft license - may require source disclosure for modifications",
    "AGPL-3.0":
      "Network copyleft license - requires source disclosure for network use",
    "AGPL-1.0":
      "Network copyleft license - requires source disclosure for network use",

    // Other potentially problematic licenses
    WTFPL: "Potentially problematic license - legal uncertainty",
    "CC-BY-SA-4.0":
      "ShareAlike license - requires derivative works to use same license",
    "CC-BY-SA-3.0":
      "ShareAlike license - requires derivative works to use same license",
    "CC-BY-NC-4.0": "Non-commercial license - prohibits commercial use",
    "CC-BY-NC-3.0": "Non-commercial license - prohibits commercial use",
    "OSL-3.0": "Copyleft license - requires derivative works to be OSL",
    "EPL-1.0": "Weak copyleft license - may require source disclosure",
    "EPL-2.0": "Weak copyleft license - may require source disclosure",
    "CDDL-1.0": "Weak copyleft license - may require source disclosure",
    "CDDL-1.1": "Weak copyleft license - may require source disclosure",
    "CPL-1.0": "Weak copyleft license - may require source disclosure",
    "MPL-1.1": "Weak copyleft license - may require source disclosure",
    "EUPL-1.1": "Copyleft license - requires derivative works to be EUPL",
    "EUPL-1.2": "Copyleft license - requires derivative works to be EUPL",
    UNLICENSED: "No license specified - usage rights unclear",
    Unknown: "License not detected - manual review required",
  };

  // Known good licenses (no warnings needed)
  const goodLicenses = new Set([
    "MIT",
    "MIT*",
    "Apache-2.0",
    "Apache License 2.0",
    "BSD-2-Clause",
    "BSD-3-Clause",
    "BSD",
    "ISC",
    "CC0-1.0",
    "Public Domain",
    "Unlicense",
    "0BSD",
    "BlueOak-1.0.0",
    "Zlib",
    "Artistic-2.0",
    "Python-2.0",
    "Ruby",
    "MPL-2.0",
    "CC-BY-4.0",
    "SEE LICENSE IN https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/proprietary/LICENSE",
    "SEE LICENSE IN LICENSE https://github.com/PostHog/posthog-js/blob/main/LICENSE",
  ]);

  // Helper function to normalize license names for comparison
  function normalizeLicense(license) {
    return license
      .replace(/-or-later$/, "") // Remove -or-later suffix
      .replace(/\+$/, "") // Remove + suffix
      .trim();
  }

  // Check each license type
  Object.entries(licenseSummary).forEach(([license, count]) => {
    // Skip known good licenses
    if (goodLicenses.has(license)) {
      return;
    }

    // Check if this license only affects our own packages
    const affectedPackages = licenseArray.filter((dep) => {
      const depLicense = Array.isArray(dep.licenseType)
        ? dep.licenseType.join(", ")
        : dep.licenseType;
      return depLicense === license;
    });

    const isOnlyOurPackages = affectedPackages.every(
      (dep) =>
        dep.name === "frontend" ||
        dep.name.toLowerCase().includes("stirling-pdf") ||
        dep.name.toLowerCase().includes("stirling_pdf") ||
        dep.name.toLowerCase().includes("stirlingpdf"),
    );

    if (
      isOnlyOurPackages &&
      (license === "UNLICENSED" || license.startsWith("SEE LICENSE IN"))
    ) {
      return; // Skip warnings for our own Stirling-PDF packages
    }

    // Check for compound licenses like "(MIT AND Zlib)" or "(MIT OR CC0-1.0)"
    if (license.includes("AND") || license.includes("OR")) {
      // For OR licenses, check if there's at least one acceptable license option
      if (license.includes("OR")) {
        // Extract license components from OR expression
        const orComponents = license
          .replace(/[()]/g, "") // Remove parentheses
          .split(" OR ")
          .map((component) => component.trim());

        // Check if any component is in the goodLicenses set (with normalization)
        const hasGoodLicense = orComponents.some((component) => {
          const normalized = normalizeLicense(component);
          return goodLicenses.has(component) || goodLicenses.has(normalized);
        });

        if (hasGoodLicense) {
          return; // Skip warning - can use the good license option
        }
      }

      // For AND licenses or OR licenses with no good options, check for problematic components
      const hasProblematicComponent = Object.keys(problematicLicenses).some(
        (problematic) => license.includes(problematic),
      );

      if (hasProblematicComponent) {
        const affectedPackages = licenseArray
          .filter((dep) => {
            const depLicense = Array.isArray(dep.licenseType)
              ? dep.licenseType.join(", ")
              : dep.licenseType;
            return depLicense === license;
          })
          .map((dep) => ({
            name: dep.name,
            version: dep.version,
            url:
              dep.repository ||
              dep.url ||
              `https://www.npmjs.com/package/${dep.name}`,
          }));

        const licenseType = license.includes("AND") ? "AND" : "OR";
        const reason =
          licenseType === "AND"
            ? "Compound license with AND requirement - all components must be compatible"
            : "Compound license with potentially problematic components and no good fallback options";

        warnings.push({
          message: `📋 This PR contains ${count} package${count > 1 ? "s" : ""} with compound license "${license}" - manual review recommended`,
          licenseType: license,
          licenseUrl: "",
          reason: reason,
          packageCount: count,
          affectedDependencies: affectedPackages,
        });
      }
      return;
    }

    // Check for exact matches with problematic licenses
    if (problematicLicenses[license]) {
      const affectedPackages = licenseArray
        .filter((dep) => {
          const depLicense = Array.isArray(dep.licenseType)
            ? dep.licenseType.join(", ")
            : dep.licenseType;
          return depLicense === license;
        })
        .map((dep) => ({
          name: dep.name,
          version: dep.version,
          url:
            dep.repository ||
            dep.url ||
            `https://www.npmjs.com/package/${dep.name}`,
        }));

      const packageList =
        affectedPackages
          .map((pkg) => pkg.name)
          .slice(0, 5)
          .join(", ") +
        (affectedPackages.length > 5
          ? `, and ${affectedPackages.length - 5} more`
          : "");
      const licenseUrl =
        getLicenseUrl(license) || "https://opensource.org/licenses";

      warnings.push({
        message: `⚠️  This PR contains ${count} package${count > 1 ? "s" : ""} with license type [${license}](${licenseUrl}) - ${problematicLicenses[license]}. Affected packages: ${packageList}`,
        licenseType: license,
        licenseUrl: licenseUrl,
        reason: problematicLicenses[license],
        packageCount: count,
        affectedDependencies: affectedPackages,
      });
    } else {
      // Unknown license type - flag for manual review
      const affectedPackages = licenseArray
        .filter((dep) => {
          const depLicense = Array.isArray(dep.licenseType)
            ? dep.licenseType.join(", ")
            : dep.licenseType;
          return depLicense === license;
        })
        .map((dep) => ({
          name: dep.name,
          version: dep.version,
          url:
            dep.repository ||
            dep.url ||
            `https://www.npmjs.com/package/${dep.name}`,
        }));

      warnings.push({
        message: `❓ This PR contains ${count} package${count > 1 ? "s" : ""} with unknown license type "${license}" - manual review required`,
        licenseType: license,
        licenseUrl: "",
        reason: "Unknown license type",
        packageCount: count,
        affectedDependencies: affectedPackages,
      });
    }
  });

  return warnings;
}
