/**
 * ESLint Rule: Page Query Must Use Builders
 *
 * Ensures page queries use builders to transform DTOs into ViewData
 * This prevents DTOs from leaking to the client
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Ensure page queries use builders to transform DTOs into ViewData',
      category: 'Page Query',
      recommended: true,
    },
    messages: {
      mustUseBuilder: 'PageQueries must use ViewDataBuilder to transform DTOs - see apps/website/lib/contracts/builders/ViewDataBuilder.ts',
      noDirectDtoReturn: 'PageQueries must not return DTOs directly, use builders to create ViewData',
    },
    schema: [],
  },

  create(context) {
    const filename = context.getFilename();
    const isPageQuery = filename.includes('/page-queries/') && filename.endsWith('.ts');

    if (!isPageQuery) {
      return {};
    }

    let hasBuilderImport = false;
    const builderUsages = [];
    const returnStatements = [];
    const builtVariables = new Set();

    return {
      // Check for builder imports
      ImportDeclaration(node) {
        const importPath = node.source.value;
        if (importPath.includes('/builders/view-data/')) {
          hasBuilderImport = true;
          
          // Track which builder is imported
          node.specifiers.forEach(spec => {
            if (spec.type === 'ImportSpecifier') {
              builderUsages.push({
                name: spec.imported.name,
                localName: spec.local.name,
              });
            }
          });
        }
      },

      // Track variable assignments from builders
      VariableDeclarator(node) {
        if (node.init && node.init.type === 'CallExpression') {
          const callee = node.init.callee;
          if (callee.type === 'MemberExpression' &&
              callee.property.type === 'Identifier' &&
              callee.property.name === 'build') {
            builtVariables.add(node.id.name);
          }
        }
      },

      // Track variable assignments from builders
      VariableDeclarator(node) {
        if (node.init && node.init.type === 'CallExpression') {
          const callee = node.init.callee;
          if (callee.type === 'MemberExpression' &&
              callee.property.type === 'Identifier' &&
              callee.property.name === 'build') {
            builtVariables.add(node.id.name);
          }
        }
      },

      // Track variable assignments from builders
      VariableDeclarator(node) {
        if (node.init && node.init.type === 'CallExpression') {
          const callee = node.init.callee;
          if (callee.type === 'MemberExpression' &&
              callee.property.type === 'Identifier' &&
              callee.property.name === 'build') {
            builtVariables.add(node.id.name);
          }
        }
      },

      // Track return statements
      ReturnStatement(node) {
        if (node.argument) {
          returnStatements.push(node);
        }
      },

      'Program:exit'() {
        if (!hasBuilderImport) {
          context.report({
            node: context.getSourceCode().ast,
            messageId: 'mustUseBuilder',
          });
          return;
        }

        // Check if return statements use builders
        returnStatements.forEach(returnNode => {
          const returnExpr = returnNode.argument;

          // Check if it's a builder call
          if (returnExpr && returnExpr.type === 'CallExpression') {
            const callee = returnExpr.callee;

            // Check if it's a builder method call (e.g., ViewDataBuilder.build())
            if (callee.type === 'MemberExpression' &&
                callee.property.type === 'Identifier' &&
                callee.property.name === 'build') {
              // This is good - using a builder
              return;
            }

            // Check if it's a direct Result.ok() with DTO
            if (callee.type === 'MemberExpression' &&
                callee.object.type === 'Identifier' &&
                callee.object.name === 'Result' &&
                callee.property.type === 'Identifier' &&
                callee.property.name === 'ok') {

              // Check if the argument is a variable that might be a DTO
              if (returnExpr.arguments && returnExpr.arguments[0]) {
                const arg = returnExpr.arguments[0];

                // If it's an identifier, check if it's likely a DTO
                if (arg.type === 'Identifier') {
                  const varName = arg.name;
                  // Skip if it's a variable built from a builder
                  if (builtVariables.has(varName)) {
                    return;
                  }
                  // Common DTO patterns: result, data, dto, apiResult, etc.
                  if (varName.match(/(result|data|dto|apiResult|response)/i)) {
                    context.report({
                      node: returnNode,
                      messageId: 'noDirectDtoReturn',
                    });
                  }
                }
              }
            }
          }
        });
      },
    };
  },
};