/**
 * ESLint rule to enforce Services follow clean architecture patterns
 *
 * Services must:
 * 1. Explicitly implement Service<TApiDto, TError> interface
 * 2. Return Result types for type-safe error handling
 * 3. Use DomainError types (not strings)
 * 4. Be classes named *Service
 * 5. Create their own dependencies (API Client, Logger, ErrorReporter)
 * 6. Have NO constructor parameters (self-contained)
 *
 * Note: Method names can vary (execute(), getSomething(), etc.)
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Enforce Services follow clean architecture patterns',
      category: 'Services',
      recommended: true,
    },
    fixable: null,
    schema: [],
    messages: {
      mustReturnResult: 'Service methods must return Promise<Result<T, DomainError>>',
      mustUseDomainError: 'Error types must be DomainError objects, not strings',
      noConstructorParams: 'Services must be self-contained. Constructor cannot have parameters. Dependencies should be created inside the constructor.',
      mustImplementContract: 'Services must explicitly implement Service<TApiDto, TError> interface',
      servicesMustBeMarked: 'Services must be explicitly marked with @Service decorator or similar (placeholder for services-must-be-marked)',
    },
  },

  create(context) {
    const filename = context.getFilename();
    const isInServices = filename.includes('/lib/services/');
    
    if (!isInServices) return {};

    let hasResultReturningMethod = false;
    let usesStringErrors = false;
    let hasConstructorParams = false;
    let classNode = null;

    return {
      ClassDeclaration(node) {
        classNode = node;
        const className = node.id?.name;
        
        if (!className || !className.endsWith('Service')) {
          return; // Not a service class
        }

        // Check if class implements Service interface
        // The implements clause can be:
        // - implements Service
        // - implements Service<TApiDto, TError>
        // The AST structure is:
        // impl.expression: Identifier { name: 'Service' }
        // impl.typeArguments: TSTypeParameterInstantiation (for generic types)
        const implementsService = node.implements && node.implements.some(impl => {
          if (impl.expression.type === 'Identifier') {
            return impl.expression.name === 'Service';
          }
          return false;
        });

        if (!implementsService) {
          context.report({
            node: node.id,
            messageId: 'mustImplementContract',
          });
        }

        // Check all methods for Result return types
        node.body.body.forEach(member => {
          if (member.type === 'MethodDefinition' &&
              member.key.type === 'Identifier') {
            
            // Check constructor parameters
            if (member.kind === 'constructor') {
              const params = member.value.params;
              if (params && params.length > 0) {
                hasConstructorParams = true;
              }
            }
            
            // Check return types
            const returnType = member.value?.returnType;
            
            if (returnType &&
                returnType.typeAnnotation &&
                returnType.typeAnnotation.typeName &&
                returnType.typeAnnotation.typeName.name === 'Promise' &&
                returnType.typeAnnotation.typeParameters &&
                returnType.typeAnnotation.typeParameters.params.length > 0) {
              
              const resultType = returnType.typeAnnotation.typeParameters.params[0];
              
              // Check for Result<...>
              if (resultType.type === 'TSTypeReference' &&
                  resultType.typeName &&
                  resultType.typeName.type === 'Identifier' &&
                  resultType.typeName.name === 'Result') {
                hasResultReturningMethod = true;
              }
              
              // Check for string error type
              if (resultType.type === 'TSTypeReference' &&
                  resultType.typeParameters &&
                  resultType.typeParameters.params.length > 1) {
                
                const errorType = resultType.typeParameters.params[1];
                // Check if error is string literal or string type
                if (errorType.type === 'TSStringKeyword' ||
                    (errorType.type === 'TSLiteralType' && errorType.literal.type === 'StringLiteral')) {
                  usesStringErrors = true;
                }
              }
            }
          }
        });
      },

      'Program:exit'() {
        if (!isInServices || !classNode) return;

        const className = classNode.id?.name;
        if (!className || !className.endsWith('Service')) return;

        // Error if constructor has parameters
        if (hasConstructorParams) {
          context.report({
            node: classNode,
            messageId: 'noConstructorParams',
          });
        }

        // Error if no methods return Result
        if (!hasResultReturningMethod) {
          context.report({
            node: classNode,
            messageId: 'mustReturnResult',
          });
        }

        // Error if using string errors
        if (usesStringErrors) {
          context.report({
            node: classNode,
            messageId: 'mustUseDomainError',
          });
        }
      },
    };
  },
};
