/**
 * ESLint Rule: Server Actions Interface Compliance
 *
 * Ensures server actions follow a specific interface pattern:
 * - Must be async functions
 * - Must use mutations for business logic
 * - Must handle errors properly using Result type
 * - Should not contain business logic directly
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Ensure server actions implement the correct interface pattern',
      category: 'Server Actions',
      recommended: true,
    },
    messages: {
      mustBeAsync: 'Server actions must be async functions',
      mustUseMutations: 'Server actions must use mutations for business logic',
      mustHandleErrors: 'Server actions must handle errors using Result type',
      noBusinessLogic: 'Server actions should be thin wrappers, business logic belongs in mutations',
      invalidActionPattern: 'Server actions should follow pattern: async function actionName(input) { const result = await mutation.execute(input); return result; }',
      missingMutationUsage: 'Server action must instantiate and call a mutation',
    },
    schema: [],
  },

  create(context) {
    const filename = context.getFilename();
    const isServerActionFile = filename.includes('/app/') && 
                               (filename.endsWith('.ts') || filename.endsWith('.tsx')) &&
                               !filename.endsWith('.test.ts') &&
                               !filename.endsWith('.test.tsx');

    if (!isServerActionFile) {
      return {};
    }

    let hasUseServerDirective = false;
    const serverActionFunctions = [];

    return {
      // Check for 'use server' directive
      ExpressionStatement(node) {
        if (node.expression.type === 'Literal' && node.expression.value === 'use server') {
          hasUseServerDirective = true;
        }
      },

      // Track async function declarations
      FunctionDeclaration(node) {
        if (node.async && node.id && node.id.name) {
          serverActionFunctions.push({
            name: node.id.name,
            node: node,
            body: node.body,
            params: node.params,
          });
        }
      },

      // Track async arrow function exports
      ExportNamedDeclaration(node) {
        if (node.declaration && node.declaration.type === 'FunctionDeclaration') {
          if (node.declaration.async && node.declaration.id) {
            serverActionFunctions.push({
              name: node.declaration.id.name,
              node: node.declaration,
              body: node.declaration.body,
              params: node.declaration.params,
            });
          }
        } else if (node.declaration && node.declaration.type === 'VariableDeclaration') {
          node.declaration.declarations.forEach(decl => {
            if (decl.init && decl.init.type === 'ArrowFunctionExpression' && decl.init.async) {
              serverActionFunctions.push({
                name: decl.id.name,
                node: decl.init,
                body: decl.init.body,
                params: decl.init.params,
              });
            }
          });
        }
      },

      'Program:exit'() {
        // Only check files with 'use server' directive
        if (!hasUseServerDirective) return;

        // Check each server action function
        serverActionFunctions.forEach(func => {
          // Check if function is async
          if (!func.node.async) {
            context.report({
              node: func.node,
              messageId: 'mustBeAsync',
            });
          }

          // Analyze function body for mutation usage and error handling
          const bodyAnalysis = analyzeFunctionBody(func.body);
          
          if (!bodyAnalysis.hasMutationUsage) {
            context.report({
              node: func.node,
              messageId: 'missingMutationUsage',
            });
          }

          if (!bodyAnalysis.hasErrorHandling) {
            context.report({
              node: func.node,
              messageId: 'mustHandleErrors',
            });
          }

          if (bodyAnalysis.hasBusinessLogic) {
            context.report({
              node: func.node,
              messageId: 'noBusinessLogic',
            });
          }

          // Check if function follows the expected pattern
          if (!bodyAnalysis.followsPattern) {
            context.report({
              node: func.node,
              messageId: 'invalidActionPattern',
            });
          }
        });
      },
    };
  },
};

/**
 * Analyze function body to check for proper patterns
 */
function analyzeFunctionBody(body) {
  const analysis = {
    hasMutationUsage: false,
    hasErrorHandling: false,
    hasBusinessLogic: false,
    followsPattern: false,
  };

  if (!body || body.type !== 'BlockStatement') {
    return analysis;
  }

  const statements = body.body;
  let hasMutationInstantiation = false;
  let hasMutationExecute = false;
  let hasResultCheck = false;
  let hasReturnResult = false;

  // Check for mutation usage in a more flexible way
  statements.forEach(stmt => {
    // Look for: const mutation = new SomeMutation()
    if (stmt.type === 'VariableDeclaration') {
      stmt.declarations.forEach(decl => {
        if (decl.init && decl.init.type === 'NewExpression') {
          if (decl.init.callee.type === 'Identifier' && 
              decl.init.callee.name.endsWith('Mutation')) {
            hasMutationInstantiation = true;
          }
        }
        
        // Look for: const result = await mutation.execute(...)
        if (decl.init && decl.init.type === 'AwaitExpression') {
          const awaitExpr = decl.init.argument;
          if (awaitExpr.type === 'CallExpression') {
            const callee = awaitExpr.callee;
            if (callee.type === 'MemberExpression' && 
                callee.property.type === 'Identifier' && 
                callee.property.name === 'execute') {
              // Check if the object is a mutation or result of mutation
              if (callee.object.type === 'Identifier') {
                const objName = callee.object.name;
                // Could be 'mutation' or 'result' (from mutation.execute)
                if (objName === 'mutation' || objName === 'result' || objName.endsWith('Mutation')) {
                  hasMutationExecute = true;
                }
              }
            }
          }
        }
      });
    }

    // Look for error handling: if (result.isErr())
    if (stmt.type === 'IfStatement') {
      if (stmt.test.type === 'CallExpression') {
        const callee = stmt.test.callee;
        if (callee.type === 'MemberExpression' && 
            callee.property.type === 'Identifier' &&
            (callee.property.name === 'isErr' || callee.property.name === 'isOk')) {
          hasResultCheck = true;
        }
      }
    }

    // Look for return statements that return Result
    if (stmt.type === 'ReturnStatement' && stmt.argument) {
      if (stmt.argument.type === 'CallExpression') {
        const callee = stmt.argument.callee;
        if (callee.type === 'MemberExpression' && 
            callee.object.type === 'Identifier' &&
            callee.object.name === 'Result') {
          hasReturnResult = true;
        }
      }
    }
  });

  analysis.hasMutationUsage = hasMutationInstantiation && hasMutationExecute;
  analysis.hasErrorHandling = hasResultCheck;
  analysis.hasReturnResult = hasReturnResult;

  // Check if follows pattern: mutation instantiation → execute → error handling → return Result
  analysis.followsPattern = analysis.hasMutationUsage && 
                           analysis.hasErrorHandling && 
                           analysis.hasReturnResult;

  // Check for business logic (direct service calls, complex calculations, etc.)
  const hasDirectServiceCall = statements.some(stmt => {
    if (stmt.type === 'ExpressionStatement' && stmt.expression.type === 'CallExpression') {
      const callee = stmt.expression.callee;
      if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier') {
        return callee.object.name.endsWith('Service');
      }
    }
    return false;
  });

  analysis.hasBusinessLogic = hasDirectServiceCall;

  return analysis;
}