import * as ts from 'typescript';
import * as Lint from 'tslint';

export class Rule extends Lint.Rules.AbstractRule {
  apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
    return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
  }
}

class Walker extends Lint.RuleWalker {
  visitClassDeclaration(node: ts.ClassDeclaration) {
    if (!node.modifiers || !this.getOptions().length) { return; }

    // Do not check the class if its abstract.
    if (!!node.modifiers.find(modifier => modifier.kind === ts.SyntaxKind.AbstractKeyword)) {
      return;
    }

    node.members
        .filter(el => el.decorators)
        .map(el => el.decorators!)
        .forEach(decorators => {
          decorators.forEach(decorator => {
            const decoratorText: string = decorator.getChildAt(1).getText();
            const matchedDecorator: string = this.getOptions().find(
              (item: string) => decoratorText.startsWith(item));
            if (!!matchedDecorator) {
              this.addFailureFromStartToEnd(decorator.getChildAt(1).pos - 1, decorator.end,
                  `The @${matchedDecorator} decorator may only be used in abstract classes. In ` +
                  `concrete classes use \`host\` in the component definition instead.`);
            }
          });
        });

    super.visitClassDeclaration(node);
  }
}
