/**
 * VL Metadata Extractor (CommonJS)
 * Parses VL project files (.vx, .sc, .cp, .vs, .vdb) from a file tree
 * and extracts structured metadata for the autotest pipeline.
 *
 * Expected input: { "relative/path": "file contents", ... }
 * Output: { apps, sections, components, services, databases }
 */

'use strict';

/**
 * Extract metadata from a VL project file tree.
 * @param {Record<string, string>} fileTree - { relativePath: fileContents }
 * @returns {{ apps: Array, sections: Array, components: Array, services: Array, databases: Array }}
 */
function extract(fileTree) {
  const apps = [];
  const sections = [];
  const components = [];
  const services = [];
  const databases = [];

  for (const [filePath, content] of Object.entries(fileTree)) {
    if (!content || typeof content !== 'string') continue;
    const ext = filePath.match(/\.([a-z]+)$/)?.[1];
    if (!ext) continue;

    try {
      switch (ext) {
        case 'vx': apps.push(parseApp(filePath, content)); break;
        case 'sc': sections.push(parseSection(filePath, content)); break;
        case 'cp': components.push(parseComponent(filePath, content)); break;
        case 'vs': services.push(parseService(filePath, content)); break;
        case 'vdb': databases.push(parseDatabase(filePath, content)); break;
      }
    } catch (e) {
      // Skip unparseable files
    }
  }

  return { apps, sections, components, services, databases };
}

/**
 * Split VL file content into sections by "# SectionName" markers.
 * Returns Map<sectionName, sectionContent>
 */
function parseSections(content) {
  const sections = {};
  const lines = content.split('\n');
  let currentName = '_header';
  let currentLines = [];

  for (const line of lines) {
    const m = line.match(/^#\s+(.+)$/);
    if (m) {
      sections[currentName] = currentLines.join('\n');
      currentName = m[1].trim();
      currentLines = [line];
    } else {
      currentLines.push(line);
    }
  }
  sections[currentName] = currentLines.join('\n');
  return sections;
}

/**
 * Extract interactive elements from a Frontend Tree section.
 */
function extractInteractiveElements(treeContent, parentSection) {
  const elements = [];
  if (!treeContent) return elements;

  const lines = treeContent.split('\n');
  for (const line of lines) {
    // Match VL element syntax: <Category-Name "instanceId"> prop:value ...
    const elMatch = line.match(/^-*<([A-Za-z]+-[A-Za-z]+)\s+"([^"]+)">(.*)/);
    if (!elMatch) continue;

    const [, typeName, instanceId, propsStr] = elMatch;
    const category = typeName.split('-')[0];
    const name = typeName;

    // Parse properties
    const props = {};
    const propMatches = propsStr.matchAll(/(\w+):(?:"([^"]*)"|(\S+))/g);
    for (const pm of propMatches) {
      props[pm[1]] = pm[2] !== undefined ? pm[2] : pm[3];
    }

    // Determine if interactive
    const isInteractive = ['Button', 'Input', 'Select', 'Textarea', 'Tag', 'Banner', 'Checkbox', 'Radio', 'Switch', 'Slider', 'DatePicker', 'Upload', 'Link'].includes(category);
    const isButton = category === 'Button';
    const isInput = category === 'Input' || category === 'Textarea';
    const isSelect = category === 'Select';

    if (isInteractive) {
      elements.push({
        instanceId,
        name,
        type: isButton ? 'button' : isInput ? 'input' : isSelect ? 'select' : category.toLowerCase(),
        label: props.value || props.label || undefined,
        placeholder: props.placeholder || undefined,
        inputType: props.inputType || (isInput ? 'text' : undefined),
        section: parentSection,
      });
    }
  }

  return elements;
}

/**
 * Extract route map from an App's Frontend Tree (If conditions with route matching).
 */
function extractRouteMap(treeContent) {
  const routeMap = {};
  if (!treeContent) return routeMap;

  const lines = treeContent.split('\n');
  let currentRoute = null;

  for (const line of lines) {
    // Match If conditions checking $route or $currentRoute
    const routeMatch = line.match(/conditions:\$(?:route|currentRoute)\s*==\s*"([^"]+)"/);
    if (routeMatch) {
      currentRoute = routeMatch[1];
      continue;
    }

    // Match Section/Component references inside route conditions
    if (currentRoute) {
      const sectionRef = line.match(/<(?:Section|Component)-(\w+)\s+"([^"]+)">/);
      if (sectionRef) {
        routeMap[currentRoute] = sectionRef[1];
        currentRoute = null;
      }
    }
  }

  return routeMap;
}

/**
 * Extract section references from an App's Frontend Tree.
 */
function extractSectionRefs(treeContent) {
  const refs = [];
  if (!treeContent) return refs;

  const lines = treeContent.split('\n');
  for (const line of lines) {
    // Match Section references: <Section-Name "instanceId">
    const m = line.match(/<Section-(\w+)\s+"([^"]+)">/);
    if (m) {
      refs.push({ sectionId: m[1], instanceId: m[2] });
    }
  }
  return refs;
}

/**
 * Extract home route from SysConfig section.
 */
function extractHomeRoute(sysConfig) {
  if (!sysConfig) return '/';
  const m = sysConfig.match(/\$(?:route|currentRoute)\(STRING\)\s*=\s*"([^"]*)"/);
  return m ? (m[1] || '/') : '/';
}

/** Parse .vx (App) file */
function parseApp(filePath, content) {
  const secs = parseSections(content);
  const fileName = filePath.replace(/^.*[\\/]/, '');
  const appId = fileName.replace(/\.vx$/, '');

  const tree = secs['Frontend Tree'] || '';
  const sectionRefs = extractSectionRefs(tree);
  const routeMap = extractRouteMap(tree);
  const homeRoute = extractHomeRoute(secs['SysConfig'] || secs['Frontend Global Vars']);

  // Build pages from section refs (simplified — one page per route or a default page)
  const pages = [];
  if (Object.keys(routeMap).length > 0) {
    for (const [route, sectionId] of Object.entries(routeMap)) {
      const matchingRefs = sectionRefs.filter(r => r.sectionId === sectionId);
      pages.push({ route, sectionRefs: matchingRefs.length > 0 ? matchingRefs : [{ sectionId, instanceId: sectionId.charAt(0).toLowerCase() + sectionId.slice(1) }] });
    }
  } else if (sectionRefs.length > 0) {
    pages.push({ route: '/', sectionRefs });
  }

  return {
    appId,
    name: appId,
    fileName,
    filePath,
    pages,
    routeMap,
    homeRoute,
    sectionRefs,
  };
}

/** Parse .sc (Section) file */
function parseSection(filePath, content) {
  const secs = parseSections(content);
  const fileName = filePath.replace(/^.*[\\/]/, '');
  const sectionId = fileName.replace(/\.sc$/, '');

  const tree = secs['Frontend Tree'] || '';
  const interactiveElements = extractInteractiveElements(tree, sectionId);

  // Extract public methods
  const methodsContent = secs['Frontend Public Methods'] || '';
  const publicMethods = [];
  const methodMatches = methodsContent.matchAll(/METHOD_PUB\s+(\w+)\(/g);
  for (const m of methodMatches) publicMethods.push(m[1]);

  // Extract public events
  const eventsContent = secs['Frontend Public Events'] || '';
  const publicEvents = [];
  const eventMatches = eventsContent.matchAll(/EVENT\s+@(\w+)/g);
  for (const m of eventMatches) publicEvents.push(m[1]);

  // Extract service domain references
  const serviceDomains = [];
  const sdMatches = tree.matchAll(/<ServiceDomain-(\w+)\s+"([^"]+)">/g);
  for (const m of sdMatches) serviceDomains.push({ name: m[1], instanceId: m[2] });

  return {
    sectionId,
    name: sectionId,
    fileName,
    filePath,
    interactiveElements,
    publicMethods,
    publicEvents,
    serviceDomains,
    elementCount: interactiveElements.length,
  };
}

/** Parse .cp (Component) file */
function parseComponent(filePath, content) {
  const secs = parseSections(content);
  const fileName = filePath.replace(/^.*[\\/]/, '');
  const componentId = fileName.replace(/\.cp$/, '');

  const tree = secs['Frontend Tree'] || '';
  const interactiveElements = extractInteractiveElements(tree, componentId);

  const publicMethods = [];
  const methodsContent = secs['Frontend Public Methods'] || '';
  const methodMatches = methodsContent.matchAll(/METHOD_PUB\s+(\w+)\(/g);
  for (const m of methodMatches) publicMethods.push(m[1]);

  const publicProps = [];
  const propsContent = secs['Frontend Public Props'] || '';
  const propMatches = propsContent.matchAll(/\$(\w+)\((\w+)\)/g);
  for (const m of propMatches) publicProps.push({ name: m[1], type: m[2] });

  return {
    componentId,
    name: componentId,
    fileName,
    filePath,
    interactiveElements,
    publicMethods,
    publicProps,
    elementCount: interactiveElements.length,
  };
}

/** Parse .vs (ServiceDomain) file */
function parseService(filePath, content) {
  const secs = parseSections(content);
  const fileName = filePath.replace(/^.*[\\/]/, '');
  const serviceId = fileName.replace(/\.vs$/, '');

  // Extract service methods
  const servicesContent = secs['Services'] || '';
  const serviceMethods = [];
  const svcMatches = servicesContent.matchAll(/SERVICE\s+(\w+)\(([^)]*)\);?\s*RETURN\s*\{([^}]*)\}/g);
  for (const m of svcMatches) {
    serviceMethods.push({
      name: m[1],
      params: m[2].trim(),
      returns: m[3].trim(),
    });
  }

  return {
    serviceId,
    domainId: serviceId,
    name: serviceId,
    fileName,
    filePath,
    serviceMethods,
  };
}

/** Parse .vdb (Database) file */
function parseDatabase(filePath, content) {
  const fileName = filePath.replace(/^.*[\\/]/, '');
  const dbId = fileName.replace(/\.vdb$/, '');

  // Extract table definitions
  const tables = [];
  const tableMatches = content.matchAll(/<VirtualTable-(\w+)\s+"([^"]+)">/g);
  for (const m of tableMatches) {
    tables.push({ name: m[1], instanceId: m[2] });
  }

  return {
    dbId,
    name: dbId,
    fileName,
    filePath,
    tables,
  };
}

module.exports = { extract, parseSections, extractInteractiveElements };
