/**
 * ESLint rule to enforce Presenter contract
 * 
 * Enforces that classes ending with "Presenter" must:
 * 1. Implement Presenter<TInput, TOutput> interface
 * 2. Have a present(input) method
 * 3. Have 'use client' directive
 */

module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Enforce Presenter contract implementation',
      category: 'Best Practices',
      recommended: true,
    },
    fixable: null,
    schema: [],
    messages: {
      missingImplements: 'Presenter class must implement Presenter<TInput, TOutput> interface',
      missingPresentMethod: 'Presenter class must have present(input) method',
      missingUseClient: 'Presenter must have \'use client\' directive at top-level',
    },
  },

  create(context) {
    const sourceCode = context.getSourceCode();
    let hasUseClient = false;
    let presenterClassNode = null;
    let hasPresentMethod = false;
    let hasImplements = false;

    return {
      // Check for 'use client' directive
      Program(node) {
        // Check comments at the top
        const comments = sourceCode.getAllComments();
        if (comments.length > 0) {
          const firstComment = comments[0];
          if (firstComment.type === 'Line' && firstComment.value.trim() === 'use client') {
            hasUseClient = true;
          } else if (firstComment.type === 'Block' && firstComment.value.includes('use client')) {
            hasUseClient = true;
          }
        }

        // Also check for 'use client' string literal as first statement
        if (node.body.length > 0) {
          const firstStmt = node.body[0];
          if (firstStmt && 
              firstStmt.type === 'ExpressionStatement' && 
              firstStmt.expression.type === 'Literal' &&
              firstStmt.expression.value === 'use client') {
            hasUseClient = true;
          }
        }
      },

      // Find Presenter classes
      ClassDeclaration(node) {
        const className = node.id?.name;
        
        // Check if this is a Presenter class
        if (className && className.endsWith('Presenter')) {
          presenterClassNode = node;
          
          // Check if it implements any interface
          if (node.implements && node.implements.length > 0) {
            for (const impl of node.implements) {
              // Handle GenericTypeAnnotation for Presenter<TInput, TOutput>
              if (impl.expression.type === 'TSInstantiationExpression') {
                const expr = impl.expression.expression;
                if (expr.type === 'Identifier' && expr.name === 'Presenter') {
                  hasImplements = true;
                }
              } else if (impl.expression.type === 'Identifier') {
                // Handle simple Presenter (without generics)
                if (impl.expression.name === 'Presenter') {
                  hasImplements = true;
                }
              }
            }
          }
        }
      },

      // Check for present method in classes
      MethodDefinition(node) {
        if (presenterClassNode && 
            node.key.type === 'Identifier' && 
            node.key.name === 'present' &&
            node.parent === presenterClassNode) {
          hasPresentMethod = true;
        }
      },

      // Report violations at the end
      'Program:exit'() {
        if (!presenterClassNode) return;

        if (!hasImplements) {
          context.report({
            node: presenterClassNode,
            messageId: 'missingImplements',
          });
        }

        if (!hasPresentMethod) {
          context.report({
            node: presenterClassNode,
            messageId: 'missingPresentMethod',
          });
        }

        if (!hasUseClient) {
          context.report({
            node: presenterClassNode,
            messageId: 'missingUseClient',
          });
        }
      },
    };
  },
};