import { getDefaultVersions } from "../resources/smith-defaultVersion.js";
import { getMasterContent } from "../resources/smith-master.js";
import { getCombinedRuleset } from "../resources/smith-ruleset.js";
import { getCombinedSource } from "../resources/smith-schemas-json.js";
import { IRuntimeInventory, MasterContent, PolicyInfo, AssetInfo, StagedPolicyGroup } from "./interfaces/IRuntimeInventory.js";




const POLICY_SEQUENCES = "policy-sequences";
const STAGED = "staged";
const FREE_FLOW = "free-flow";
const POLICIES = "policies";
const DEFAULT_VERSION_KEY = "defaultVersion";

export enum CommonKind {
  Api = "api",
  Plan = "plan",
  Product = "product",
  TestApi = "testapi",
  Tuple = "tuple",
  UriSchemes = "urischemes"
}

const kindEnums = [
                        "API",
                        "Scope",
                        "Project",
                        "StagedPolicySequence",
                        "InvokeAWSLambda",
                        "ValidateAPISpecification",
                        "CORS",
                        "Quota",
                        "Plan",
                        "Product",
                        "URISchemes",
                        "properties",
                        "Telemetry",
                        "Properties",
                        "LoadBalancer",
                        "SetAuthorization",
                        "Invoke",
                        "GlobalPolicy",
                        "InboundBulkHead",
                        "SetMediaType",
                        "InboundMessaging",
                        "IAM",
                        "AuthorizeUser",
                        "SetContextVariable",
                        "WebMethodsISService",
                        "Log",
                        "MonitorTraffic",
                        "CacheServiceResult",
                        "OutboundAlias",
                        "OutboundAnonymous",
                        "HTTPInvoke",
                        "InvokeMessagingExtension",
                        "DataMasking",
                        "TransformRequest",
                        "TransformResponse",
                        "Route",
                        "MessageConfig",
                        "HTTPEndpoint",
                        "MockEndpoint",
                        "MockResponse",
                        "ErrorProcessing",
                        "Set",
                        "RateLimitDef",
                        "RateLimit",
                        "Redact",
                        "Remove",
                        "Transform",
                        "DataPowerAssembly",
                        "Switch",
                        "If",
                        "OperationSwitch",
                        "Try",
                        "IBMCloudLogin",
                        "WatsonXAIInvoke",
                        "OpenAIInvoke",
                        "FreeFlowPolicySequence",
                        "Block",
                        "TokenMediation",
                        "EnforceCircuitBreaker",
                        "JavaScript",
                        "LuaScript",
                        "Cache",
                        "Antivirus",
                        "SQLInjectionFilter",
                        "CountLimit",
                        "CountLimitDef",
                        "Return",
                        "Retry",
                        "Throw",
                        "HandlebarsTemplate",
                        "ExtractIdentity",
                        "Authorize",
                        "Authenticate",
                        "Parse",
                        "test",
                        "assertion",
                        "environment"
                      ];

const generic_rules = {
  "rules": {
    "invalid-kind-value-combined": {
          "description": `Kind must be one of ${kindEnums.map(k => `'${k}'`).join(' | ')}`,
          "severity": "error",
          "given": "$",
          "then": {
            "field": "kind",
            "function": "schema",
            "functionOptions": {
              "schema": {
                "type": "string",
                "enum": kindEnums
              }
            }
          }
        },
    "kind-not-exist": {
      "description": `Kind does not exist.`,
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "kind",
        "function": "truthy"
      }
    },
    "invalid-kind-spl-character": {
      "description": "kind should not be having empty or special characters",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "kind",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "invalid-api-version": {
      "description": "apiVersion must be one of the valid values 'api.ibm.com/v1'",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "apiVersion",
        "function": "schema",
        "functionOptions": {
          "schema": {
            "type": "string",
            "enum": [
              "api.ibm.com/v1"
            ]
          }
        }
      }
    },
    "api-version-not-exist": {
      "description": "apiVersion does not exist.",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "apiVersion",
        "function": "truthy"
      }
    },
    "metadata-not-exist": {
      "description": "Metadata does not exist.",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "metadata",
        "function": "truthy"
      }
    },
    "metadata-whitelist-check": {
      "description": "Metadata should not be having empty or special characters",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "metadata",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "metadata-name-not-exist": {
      "description": "Metadata name does not exist",
      "severity": "error",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "name",
        "function": "truthy"
      }
    },
    "metadata-name-whitelist-check": {
      "description": "Metadata name should not be having empty or special characters",
      "severity": "error",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "name",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "metadata-version-not-exist": {
      "description": "Metadata version does not exist",
      "severity": "error",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "version",
        "function": "truthy"
      }
    },
    "metadata-version-whitelist-check": {
      "description": "Metadata version should not be having empty or special characters",
      "severity": "error",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "version",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "metadata-namespace-not-exist": {
      "description": "Metadata namespace does not exist",
      "severity": "warn",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "namespace",
        "function": "truthy"
      }
    },
    "metadata-namespace-whitelist-check": {
      "description": "Metadata namespace should not be having empty or special characters",
      "severity": "warn",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "namespace",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "spec-details-not-exist": {
      "description": "Spec details does not exist",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "spec",
        "function": "truthy"
      }
    },
    "spec-details-whitelist-check": {
      "description": "Spec should not be having empty or special characters",
      "severity": "error",
      "given": "$",
      "resolved": false,
      "then": {
        "field": "spec",
        "function": "pattern",
        "functionOptions": {
          "match": "^(?![\\s\\W_]+$).+$"
        }
      }
    },
    "tags-not-exist": {
      "description": "Tag does not exist",
      "severity": "warn",
      "given": "$.metadata",
      "resolved": false,
      "then": {
        "field": "tags",
        "function": "truthy"
      }
    },
    "invalid-tag-type": {
      "description": "Invalid Tag Type",
      "severity": "error",
      "given": "$.metadata.tags",
      "resolved": false,
      "then": {
        "function": "schema",
        "functionOptions": {
          "schema": {
            "type": "array"
          }
        }
      }
    }
  }
};

export type CommonKindType = `${CommonKind}`;

export class RuntimeInventory implements IRuntimeInventory {
  public masterContent: MasterContent;
  public schemaDefinitions: Record<string, any>;
  public defaultVersionMap: Record<string, string>;
  private rulesetDefinitions: Record<string, any>;

  constructor() {
    try {
      // Initialize with the imported data
      this.schemaDefinitions = getCombinedSource();
      this.rulesetDefinitions = getCombinedRuleset();
      this.defaultVersionMap = getDefaultVersions();
      this.masterContent = getMasterContent();

      // Log successful initialization
    } catch (error) {
      this.schemaDefinitions = {};
      this.rulesetDefinitions = {};
      this.defaultVersionMap = {};
      this.masterContent = {};
    }
  }

  /**
   * Overrides the master content with new data
   * @param newMasterContent - New master content to replace the existing data
   */
  public setMasterContent(newMasterContent: MasterContent): void {
    if (!newMasterContent) return;
    this.masterContent = newMasterContent;
  }

  /**
   * Extends or overrides the master content with custom data
   * @param customMasterContent - Custom master content to merge with existing data
   * @param overrideExisting - If true, will override existing entries; if false, will only add new entries
   */
  public extendMasterContent(customMasterContent: Partial<MasterContent>, overrideExisting: boolean = false): void {
    if (!customMasterContent) return;

    // For complete replacement, use setMasterContent instead
    if (overrideExisting && Object.keys(customMasterContent).length > 0) {
      this.setMasterContent(customMasterContent as MasterContent);
      return;
    }

    // Handle policy sequences
    if (customMasterContent['policy-sequences']) {
      if (!this.masterContent['policy-sequences']) {
        this.masterContent['policy-sequences'] = {};
      }

      // Handle staged policies
      if (customMasterContent['policy-sequences']?.staged) {
        if (!this.masterContent['policy-sequences']?.staged) {
          if (!this.masterContent['policy-sequences']) {
            this.masterContent['policy-sequences'] = {};
          }
          this.masterContent['policy-sequences'].staged = [];
        }

        const stagedPolicies = this.masterContent['policy-sequences'].staged as StagedPolicyGroup[];

        // For each custom staged policy group
        customMasterContent['policy-sequences'].staged.forEach(customGroup => {
          if (!customGroup.key) return;

          // Find if this group already exists
          const existingGroupIndex = stagedPolicies.findIndex(
            group => group.key === customGroup.key
          );

          if (existingGroupIndex >= 0 && overrideExisting) {
            // Replace existing group
            stagedPolicies[existingGroupIndex] = customGroup;
          } else if (existingGroupIndex < 0) {
            // Add new group
            stagedPolicies.push(customGroup);
          } else if (existingGroupIndex >= 0 && !overrideExisting && customGroup.assets) {
            // Merge assets into existing group
            const existingGroup = stagedPolicies[existingGroupIndex];
            if (!existingGroup.assets) existingGroup.assets = [];

            customGroup.assets.forEach(asset => {
              const existingAssetIndex = existingGroup.assets!.findIndex(a => a.kind === asset.kind);
              if (existingAssetIndex >= 0 && overrideExisting) {
                existingGroup.assets![existingAssetIndex] = asset;
              } else if (existingAssetIndex < 0) {
                existingGroup.assets!.push(asset);
              }
            });
          }
        });
      }

      // Handle free-flow policies
      if (customMasterContent['policy-sequences']?.['free-flow']) {
        if (!this.masterContent['policy-sequences']?.['free-flow']) {
          if (!this.masterContent['policy-sequences']) {
            this.masterContent['policy-sequences'] = {};
          }
          this.masterContent['policy-sequences']['free-flow'] = [];
        }

        const freeFlowPolicies = this.masterContent['policy-sequences']['free-flow'] as any[];

        customMasterContent['policy-sequences']['free-flow'].forEach(customGroup => {
          if (!customGroup.name) return;

          const existingGroupIndex = freeFlowPolicies.findIndex(
            group => group.name === customGroup.name
          );

          if (existingGroupIndex >= 0 && overrideExisting) {
            freeFlowPolicies[existingGroupIndex] = customGroup;
          } else if (existingGroupIndex < 0) {
            freeFlowPolicies.push(customGroup);
          }
        });
      }
    }

    // Handle other top-level properties
    Object.entries(customMasterContent).forEach(([key, value]) => {
      if (key !== 'policy-sequences') {
        if (overrideExisting || !this.masterContent[key]) {
          this.masterContent[key] = value;
        }
      }
    });
  }

  /**
   * Extends or overrides the schema definitions with custom schemas
   * @param customSchemas - Custom schemas to merge with existing schemas
   * @param overrideExisting - If true, will override existing schemas; if false, will only add new schemas
   */
  public extendSchemaDefinitions(customSchemas: Record<string, any>, overrideExisting: boolean = false): void {
    if (!customSchemas) return;

    Object.entries(customSchemas).forEach(([key, value]) => {
      if (overrideExisting || !this.schemaDefinitions[key]) {
        this.schemaDefinitions[key] = value;
      }
    });
  }

  /**
   * Extends or overrides the default versions with custom default versions
   * @param customDefaultVersions - Custom default versions to merge with existing versions
   * @param overrideExisting - If true, will override existing versions; if false, will only add new versions
   */
  public extendDefaultVersions(customDefaultVersions: Record<string, string>, overrideExisting: boolean = false): void {
    if (!customDefaultVersions) return;

    Object.entries(customDefaultVersions).forEach(([key, value]) => {
      if (overrideExisting || !this.defaultVersionMap[key]) {
        this.defaultVersionMap[key] = value;
      }
    });
  }

  /**
   * Extends or overrides the ruleset definitions with custom rulesets
   * @param customRulesets - Custom rulesets to merge with existing rulesets
   * @param overrideExisting - If true, will override existing rulesets; if false, will only add new rulesets
   */
  public extendRulesetDefinitions(customRulesets: Record<string, any>, overrideExisting: boolean = false): void {
    if (!customRulesets) return;

    Object.entries(customRulesets).forEach(([key, value]) => {
      if (overrideExisting || !this.rulesetDefinitions[key]) {
        this.rulesetDefinitions[key] = value;
      }
    });
  }

  /**
   * Hook method for subclasses to provide overridden schema for a specific schema key
   * This is called automatically by getSchema() to check if there's an override
   * Extended classes can override this to provide custom schemas
   * @param schemaKey - The schema key (e.g., "api.ibm.com_v1_customkind.json")
   * @returns The overridden schema object or undefined if no override
   */
  public getOverriddenSchema(schemaKey: string): any | undefined {
    // Base implementation returns undefined (no override)
    // Subclasses can override to provide custom schemas
    return undefined;
  }

  /**
   * Hook method for subclasses to provide overridden ruleset for a specific ruleset key
   * This is called automatically by getLintRuleset() to check if there's an override
   * Extended classes can override this to provide custom rulesets
   * @param rulesetKey - The ruleset key (e.g., "api.ibm.com_v1_customkind.ruleset.yaml")
   * @returns The overridden ruleset object or undefined if no override
   */
  public getOverriddenRule(rulesetKey: string): any | undefined {
    // Base implementation returns undefined (no override)
    // Subclasses can override to provide custom rulesets
    return undefined;
  }

  public getSchema(
    name: string,
    version?: string
  ): any | undefined {
    try {

      const kindLower = name.toLowerCase();

      if (!version) {
        // Get default version from the defaultVersion.json
        const matchedKey = Object.keys(this.defaultVersionMap).find(
          (key) => key.toLowerCase() === name.toLowerCase()
        );

        if (matchedKey) {
          version = this.defaultVersionMap[matchedKey];

        } else {

        }

        if (!version) {
          return undefined;
        }
      }

      // Format the key to match the combined-source.json format
      version = version.replace(/\//g, "_");
      const schemaKey = `${version}_${kindLower}.json`;

      if(this.getOverriddenSchema(schemaKey)) 
        {
          return this.getOverriddenSchema(schemaKey);
        }
      else if (this.schemaDefinitions[schemaKey]) {
        return this.schemaDefinitions[schemaKey];
      } else {
        return undefined;
      }
    } catch (error) {
      console.error(
        `Error retrieving schema for ${name} version ${version}:`,
        error
      );
      return undefined;
    }
  }

  public getSchemaFromDestination(
    name: string,
    version?: string
  ): any | undefined {
    try {
      const kindLower = name.toLowerCase();

      if (!version) {
        // Get default version from the defaultVersion.json
        const matchedKey = Object.keys(this.defaultVersionMap).find(
          (key) => key.toLowerCase() === name.toLowerCase()
        );

        if (matchedKey) {
          version = this.defaultVersionMap[matchedKey];
        }
        if (!version) {
          return undefined;
        }
      }

      // Format the key to match the combined-source.json format
      version = version.replace(/\//g, "_");
      const schemaKey = `${version}_${kindLower}.json`;

      // For destination schemas, we would need to implement a similar approach
      // Currently, destination schemas are not included in combined-source.json
      // This is a placeholder for future implementation

      return undefined;
    } catch (error) {
      console.error(
        `Error retrieving schema for ${name} version ${version} in destination:`,
        error
      );
      return undefined;
    }
  }

  public getTypescript(name: string, version?: string): Record<string, any> | undefined {
    try {
      const kindLower = name.toLowerCase();

      if (!version) {
        // Get default version from the defaultVersion.json
        const matchedKey = Object.keys(this.defaultVersionMap).find(
          (key) => key.toLowerCase() === name.toLowerCase()
        );

        if (matchedKey) {
          version = this.defaultVersionMap[matchedKey];
        }

        if (!version) {
          return undefined;
        }
      }

      // Format the key to match the combined-source.json format
      version = version.replace(/\//g, "_");
      const schemaKey = `${version}_${kindLower}.json`;

      // TypeScript definitions are not included in combined-source.json
      // This is a placeholder for future implementation
      return undefined;
    } catch (error) {
      console.error(
        `Error retrieving TypeScript for ${name} version ${version}:`,
        error
      );
      return undefined;
    }
  }

  public getLintRuleset(name: string, version?: string): Record<string, any> | undefined {
    try {
      const kindLower = name.toLowerCase();

      if (!version) {
        // Get default version from the defaultVersion.json
        const matchedKey = Object.keys(this.defaultVersionMap).find(
          (key) => key.toLowerCase() === name.toLowerCase()
        );

        if (matchedKey) {
          version = this.defaultVersionMap[matchedKey];
        }

        if (!version) {
          return undefined;
        }
      }

      // Format the key to match the combined ruleset format
      version = version.replace(/\//g, "_");
      const rulesetKey = `${version}_${kindLower}.ruleset.yaml`;

       if(this.getOverriddenRule(rulesetKey)) 
       {
        return this.getOverriddenRule(rulesetKey)
       }
      // Look for the ruleset in the ruleset definitions
      else if (this.rulesetDefinitions[rulesetKey]) {
        // Return the ruleset object directly
        return this.rulesetDefinitions[rulesetKey];
      } else {
        return generic_rules;
      }
    } catch (error) {
      console.error(
        `Error retrieving lint ruleset for ${name} version ${version}:`,
        error
      );
      return undefined;
    }
  }

  public getPolicySequenceType(): { sequenceTypes: string[] } | undefined {
    try {
      const sequenceTypes: string[] = [];

      const stagedPolicies = this.getStagedPolicies();
      if (stagedPolicies && Object.keys(stagedPolicies).length > 0) {
        sequenceTypes.push(STAGED);
      }

      const freeFlowPolicies = this.getFreeFlowPolicies();
      if (freeFlowPolicies && Object.keys(freeFlowPolicies).length > 0) {
        sequenceTypes.push(FREE_FLOW);
      }

      return sequenceTypes.length > 0 ? { sequenceTypes } : undefined;
    } catch (error) {
      console.error("Error getting policy sequence types:", error);
      return undefined;
    }
  }

  public getStagedPolicies():
    | Record<string, { stage: string; policies: PolicyInfo[] }>
    | undefined {
    try {
      const staged = this.masterContent?.[POLICY_SEQUENCES]?.[STAGED];
      if (staged && Array.isArray(staged)) {
        const result: Record<
          string,
          { stage: string; policies: PolicyInfo[] }
        > = {};

        for (const stageGroup of staged) {
          const stageName = stageGroup.key;
          const assets = stageGroup.assets || [];

          result[stageName] = {
            stage: stageName,
            policies: assets.map((asset: AssetInfo) => ({
              name: asset.kind,
              defaultVersion: asset.defautlVersion || "api.ibm.com/v1",
              type: STAGED,
            })),
          };
        }
        return result;
      }
      return undefined;
    } catch (error) {
      console.error("Error getting staged policies:", error);
      return undefined;
    }
  }

  public getFreeFlowPolicies():
    | Record<string, { group: string; type: string; policies: PolicyInfo[] }>
    | undefined {
    try {
      const freeFlow = this.masterContent?.[POLICY_SEQUENCES]?.[FREE_FLOW];
      if (!Array.isArray(freeFlow)) {
        return undefined;
      }

      const result: Record<
        string,
        { group: string; type: string; policies: PolicyInfo[] }
      > = {};

      freeFlow.forEach((group, index) => {
        const groupName = group.name || `group_${index}`;
        const flattenedPolicies = this.flattenPolicies(group[POLICIES] || []);

        result[groupName] = {
          group: groupName,
          type: group.type,
          policies: flattenedPolicies.map((policy: any) => ({
            name: policy.name,
            defaultVersion: policy[DEFAULT_VERSION_KEY] || "1.0.0",
            type: FREE_FLOW,
          })),
        };
      });

      return result;
    } catch (error) {
      console.error("Error getting free flow policies:", error);
      return undefined;
    }
  }

  public getPolicyDefaultVersion(
    sequenceType: typeof STAGED | typeof FREE_FLOW,
    groupName: string,
    policyName: string
  ): string | undefined {
    try {
      if (sequenceType === STAGED) {
        const stagedGroups = this.masterContent[POLICY_SEQUENCES]?.[STAGED];
        if (stagedGroups && Array.isArray(stagedGroups)) {
          const group = stagedGroups.find(g => g.key === groupName);
          if (group && group.assets) {
            const asset = group.assets.find(a => a.kind === policyName);
            if (asset) {
              return asset.defautlVersion || "api.ibm.com/v1";
            }
          }
        }
      } else if (sequenceType === FREE_FLOW) {
        const freeFlow = this.masterContent[POLICY_SEQUENCES]?.[FREE_FLOW];
        if (Array.isArray(freeFlow)) {
          for (const group of freeFlow) {
            if (group.name === groupName && group[POLICIES]) {
              for (const policy of this.flattenPolicies(group[POLICIES])) {
                if (policy.name === policyName) {
                  return policy[DEFAULT_VERSION_KEY] || "1.0.0";
                }
              }
            }
          }
        }
      }
      return undefined;
    } catch (error) {
      console.error(
        `Error getting policy default version for ${sequenceType}/${groupName}/${policyName}:`,
        error
      );
      return undefined;
    }
  }

  public getMasterContents(): MasterContent {
    // Create a copy of masterContent to ensure we don't modify the original
    const masterContentCopy = { ...this.masterContent };

    // Ensure assetProperties is included in the returned object
    if (this.masterContent.assetProperties) {
      masterContentCopy.assetProperties = this.masterContent.assetProperties;
    }

    return masterContentCopy;
  }

  /**
   * Get the complete default versions mapping
   * @returns A record mapping kind names to their default API versions
   */
  public getDefaultVersions(): Record<string, string> {
    // Return a copy of the default versions map to prevent direct modification
    return { ...this.defaultVersionMap };
  }

  /**
   * Get the list of required kinds
   * @returns An array of required kind names
   */
  public getRequiredKinds(): string[] {
    // Return the requiredKinds array from masterContent, or an empty array if not defined
    return this.masterContent.requiredKinds || [];
  }

  /**
   * Get the list of optional kinds
   * @returns An array of optional kind names
   */
  public getOptionalKinds(): string[] {
    // Return the optionalKinds array from masterContent, or an empty array if not defined
    return this.masterContent.optionalKinds || [];
  }

  public getPolicyInfo(
    sequenceType: typeof STAGED | typeof FREE_FLOW,
    groupName: string,
    policyName: string
  ) {
    try {
      if (sequenceType === STAGED) {
        const stagedGroups = this.masterContent[POLICY_SEQUENCES]?.[STAGED];
        if (stagedGroups && Array.isArray(stagedGroups)) {
          const group = stagedGroups.find(g => g.key === groupName);
          if (group && group.assets) {
            const asset = group.assets.find(a => a.kind === policyName);
            if (asset) {
              return {
                name: policyName,
                sequenceType: STAGED,
                group: groupName,
                defaultVersion: asset.defautlVersion || "api.ibm.com/v1",
                policy: asset,
              };
            }
          }
        }
      } else if (sequenceType === FREE_FLOW) {
        const freeFlow = this.masterContent[POLICY_SEQUENCES]?.[FREE_FLOW];
        if (Array.isArray(freeFlow)) {
          for (const group of freeFlow) {
            if (group.name === groupName && group[POLICIES]) {
              for (const policy of this.flattenPolicies(group[POLICIES])) {
                if (policy.name === policyName) {
                  return {
                    name: policyName,
                    sequenceType: FREE_FLOW,
                    group: groupName,
                    defaultVersion: policy[DEFAULT_VERSION_KEY] || "1.0.0",
                    policy,
                  };
                }
              }
            }
          }
        }
      }
      return undefined;
    } catch (error) {
      console.error(
        `Error getting policy info for ${sequenceType}/${groupName}/${policyName}:`,
        error
      );
      return undefined;
    }
  }

  private flattenPolicies(policies: any[]): any[] {
    let result: any[] = [];
    for (const item of policies) {
      if (item.type === "policy") {
        result.push(item);
      } else if (item.type === "group" && item[POLICIES]) {
        result = result.concat(this.flattenPolicies(item[POLICIES]));
      }
    }
    return result;
  }
}

export const runtimeInventory = new RuntimeInventory();
