#!/usr/bin/env node

const fs = require("fs");
const path = require("path");

// Color output helpers
const colors = {
  red: "\x1b[31m",
  green: "\x1b[32m",
  yellow: "\x1b[33m",
  blue: "\x1b[34m",
  reset: "\x1b[0m",
};

function log(message, color = "reset") {
  console.log(`${colors[color]}${message}${colors.reset}`);
}

function error(message) {
  log(`❌ ${message}`, "red");
}

function success(message) {
  log(`✅ ${message}`, "green");
}

function warn(message) {
  log(`⚠️  ${message}`, "yellow");
}

function info(message) {
  log(`ℹ️  ${message}`, "blue");
}

// Plugin schema validation
function validatePlugin(pluginPath) {
  if (!fs.existsSync(pluginPath)) {
    throw new Error(`Plugin file not found: ${pluginPath}`);
  }

  let plugin;
  try {
    plugin = JSON.parse(fs.readFileSync(pluginPath, "utf8"));
  } catch (e) {
    throw new Error(`Invalid JSON syntax: ${e.message}`);
  }

  const errors = [];
  const warnings = [];

  // Required fields validation
  const requiredFields = [
    "robotId",
    "name",
    "platform",
    "version",
    "pluginApiVersion",
    "hriStudioVersion",
    "trustLevel",
    "category",
  ];

  requiredFields.forEach((field) => {
    if (!plugin[field]) {
      errors.push(`Missing required field: ${field}`);
    }
  });

  // Field format validation
  if (plugin.robotId && !/^[a-z0-9-]+$/.test(plugin.robotId)) {
    errors.push("robotId must be lowercase with hyphens only");
  }

  if (plugin.version && !/^\d+\.\d+\.\d+/.test(plugin.version)) {
    errors.push("version must follow semantic versioning (e.g., 1.0.0)");
  }

  if (
    plugin.trustLevel &&
    !["official", "verified", "community"].includes(plugin.trustLevel)
  ) {
    errors.push(
      `Invalid trustLevel: ${plugin.trustLevel}. Must be: official, verified, or community`,
    );
  }

  // Category validation
  const validCategories = [
    "mobile-robot",
    "humanoid-robot",
    "manipulator",
    "drone",
    "sensor-platform",
    "simulation",
  ];

  if (plugin.category && !validCategories.includes(plugin.category)) {
    errors.push(
      `Invalid category: ${plugin.category}. Valid categories: ${validCategories.join(", ")}`,
    );
  }

  // Actions validation
  if (!plugin.actions || !Array.isArray(plugin.actions)) {
    errors.push("Plugin must have an actions array");
  } else if (plugin.actions.length === 0) {
    warnings.push("Plugin has no actions defined");
  } else {
    plugin.actions.forEach((action, index) => {
      const actionErrors = validateAction(action, index);
      errors.push(...actionErrors);
    });
  }

  // Assets validation
  if (plugin.assets) {
    if (!plugin.assets.thumbnailUrl) {
      errors.push("assets.thumbnailUrl is required");
    }

    // Check if asset paths exist
    const assetChecks = [
      ["thumbnailUrl", plugin.assets.thumbnailUrl],
      ["main image", plugin.assets.images?.main],
      ["logo", plugin.assets.images?.logo],
    ];

    if (plugin.assets.images?.angles) {
      Object.entries(plugin.assets.images.angles).forEach(
        ([angle, assetPath]) => {
          assetChecks.push([`${angle} angle`, assetPath]);
        },
      );
    }

    assetChecks.forEach(([description, assetPath]) => {
      if (assetPath && assetPath.startsWith("assets/")) {
        const fullPath = path.resolve(
          path.dirname(pluginPath),
          "..",
          assetPath,
        );
        if (!fs.existsSync(fullPath)) {
          warnings.push(`Asset not found: ${description} (${assetPath})`);
        }
      }
    });
  } else {
    errors.push("Plugin must have assets definition");
  }

  // Manufacturer validation
  if (!plugin.manufacturer?.name) {
    warnings.push("manufacturer.name is recommended");
  }

  return { errors, warnings, plugin };
}

function validateAction(action, index) {
  const errors = [];

  // Required action fields
  const requiredFields = ["id", "name", "category", "parameterSchema"];
  requiredFields.forEach((field) => {
    if (!action[field]) {
      errors.push(`Action ${index}: missing required field '${field}'`);
    }
  });

  // Action ID format
  if (action.id && !/^[a-z_]+$/.test(action.id)) {
    errors.push(
      `Action ${index}: id must be snake_case (lowercase with underscores)`,
    );
  }

  // Action category validation
  const validActionCategories = ["movement", "interaction", "sensors", "logic"];
  if (action.category && !validActionCategories.includes(action.category)) {
    errors.push(
      `Action ${index}: invalid category '${action.category}'. Valid: ${validActionCategories.join(", ")}`,
    );
  }

  // Parameter schema validation
  if (action.parameterSchema) {
    if (action.parameterSchema.type !== "object") {
      errors.push(`Action ${index}: parameterSchema.type must be 'object'`);
    }

    if (!action.parameterSchema.properties) {
      errors.push(`Action ${index}: parameterSchema must have properties`);
    }

    if (!Array.isArray(action.parameterSchema.required)) {
      errors.push(`Action ${index}: parameterSchema.required must be an array`);
    }
  }

  // Communication protocol validation
  const hasRos2 = action.ros2;
  const hasNaoqi = action.naoqi;
  const hasRestApi = action.restApi;

  if (!hasRos2 && !hasNaoqi && !hasRestApi) {
    errors.push(
      `Action ${index}: must have at least one communication protocol (ros2, naoqi, or restApi)`,
    );
  }

  return errors;
}

// Repository validation
function validateRepository() {
  const repoPath = path.resolve("repository.json");

  if (!fs.existsSync(repoPath)) {
    throw new Error("repository.json not found");
  }

  let repo;
  try {
    repo = JSON.parse(fs.readFileSync(repoPath, "utf8"));
  } catch (e) {
    throw new Error(`Invalid repository.json: ${e.message}`);
  }

  const errors = [];
  const warnings = [];

  // Required repository fields
  const requiredFields = [
    "id",
    "name",
    "apiVersion",
    "pluginApiVersion",
    "trust",
  ];
  requiredFields.forEach((field) => {
    if (!repo[field]) {
      errors.push(`Missing required repository field: ${field}`);
    }
  });

  // Validate plugin count
  const indexPath = path.resolve("plugins/index.json");
  if (fs.existsSync(indexPath)) {
    const index = JSON.parse(fs.readFileSync(indexPath, "utf8"));
    const actualCount = index.length;
    const reportedCount = repo.stats?.plugins || 0;

    if (actualCount !== reportedCount) {
      errors.push(
        `Plugin count mismatch: reported ${reportedCount}, actual ${actualCount}`,
      );
    }
  }

  return { errors, warnings, repo };
}

// Update plugin index
function updateIndex() {
  const pluginsDir = path.resolve("plugins");
  const indexPath = path.join(pluginsDir, "index.json");

  if (!fs.existsSync(pluginsDir)) {
    throw new Error("plugins directory not found");
  }

  const pluginFiles = fs
    .readdirSync(pluginsDir)
    .filter((file) => file.endsWith(".json") && file !== "index.json")
    .sort();

  fs.writeFileSync(indexPath, JSON.stringify(pluginFiles, null, 2));
  success(`Updated index.json with ${pluginFiles.length} plugins`);

  // Update repository stats
  const repoPath = path.resolve("repository.json");
  if (fs.existsSync(repoPath)) {
    const repo = JSON.parse(fs.readFileSync(repoPath, "utf8"));
    repo.stats = repo.stats || {};
    repo.stats.plugins = pluginFiles.length;
    fs.writeFileSync(repoPath, JSON.stringify(repo, null, 2));
    success(`Updated repository stats: ${pluginFiles.length} plugins`);
  }
}

// Main CLI
function main() {
  const args = process.argv.slice(2);
  const command = args[0];

  try {
    switch (command) {
      case "validate":
        const pluginPath = args[1];
        if (!pluginPath) {
          error("Usage: validate <plugin-file>");
          process.exit(1);
        }

        info(`Validating plugin: ${pluginPath}`);
        const { errors, warnings } = validatePlugin(pluginPath);

        if (errors.length > 0) {
          error("Validation failed:");
          errors.forEach((err) => console.log(`  - ${err}`));
        }

        if (warnings.length > 0) {
          warn("Warnings:");
          warnings.forEach((warn) => console.log(`  - ${warn}`));
        }

        if (errors.length === 0) {
          success("Plugin validation passed!");
          if (warnings.length === 0) {
            success("No warnings found");
          }
        } else {
          process.exit(1);
        }
        break;

      case "validate-all":
        info("Validating all plugins...");

        // Validate repository
        const repoResult = validateRepository();
        if (repoResult.errors.length > 0) {
          error("Repository validation failed:");
          repoResult.errors.forEach((err) => console.log(`  - ${err}`));
          process.exit(1);
        }

        // Validate all plugins
        const indexPath = path.resolve("plugins/index.json");
        if (!fs.existsSync(indexPath)) {
          error("plugins/index.json not found");
          process.exit(1);
        }

        const index = JSON.parse(fs.readFileSync(indexPath, "utf8"));
        let allValid = true;

        for (const pluginFile of index) {
          const pluginPath = path.resolve("plugins", pluginFile);
          try {
            const { errors } = validatePlugin(pluginPath);
            if (errors.length > 0) {
              error(`${pluginFile}: ${errors.length} errors`);
              errors.forEach((err) => console.log(`    - ${err}`));
              allValid = false;
            } else {
              success(`${pluginFile}: valid`);
            }
          } catch (e) {
            error(`${pluginFile}: ${e.message}`);
            allValid = false;
          }
        }

        if (allValid) {
          success("All plugins are valid!");
        } else {
          process.exit(1);
        }
        break;

      case "update-index":
        info("Updating plugin index...");
        updateIndex();
        break;

      case "help":
      default:
        console.log(`
HRIStudio Plugin Validator

Usage:
  validate <plugin-file>    Validate a single plugin file
  validate-all             Validate all plugins and repository
  update-index            Update plugins/index.json with all plugin files
  help                    Show this help message

Examples:
  ./scripts/validate-plugin.js validate plugins/my-robot.json
  ./scripts/validate-plugin.js validate-all
  ./scripts/validate-plugin.js update-index
        `);
        break;
    }
  } catch (e) {
    error(`Error: ${e.message}`);
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}

module.exports = {
  validatePlugin,
  validateRepository,
  updateIndex,
};
