import { DocCollection, Processor } from 'dgeni';
import { ApiDoc } from 'dgeni-packages/typescript/api-doc-types/ApiDoc';
import { ClassExportDoc } from 'dgeni-packages/typescript/api-doc-types/ClassExportDoc';
import { MemberDoc } from 'dgeni-packages/typescript/api-doc-types/MemberDoc';

const INTERNAL_METHODS = [
    // Lifecycle methods
    'ngOnInit',
    'ngOnChanges',
    'ngDoCheck',
    'ngAfterContentInit',
    'ngAfterContentChecked',
    'ngAfterViewInit',
    'ngAfterViewChecked',
    'ngOnDestroy',

    // ControlValueAccessor methods
    'writeValue',
    'registerOnChange',
    'registerOnTouched',
    'setDisabledState',

    // Don't ever need to document constructors
    'constructor',

    // tabIndex exists on all elements, no need to document it
    'tabIndex',

    // override is a special typescript keyword (it gets printed out as property for some reason)
    'override',
];

/**
 * Processor to filter out symbols that should not be shown in the Material docs.
 */
export class DocsPrivateFilter implements Processor {
    name = 'docs-private-filter';
    $runBefore = ['categorizer'];

    $process(docs: DocCollection) {
        return docs.filter(doc => this.isPublicDoc(doc));
    }

    /** Marks the given API doc with a property that describes its public state. */
    private isPublicDoc(doc: ApiDoc) {
        if (this.hasDocsPrivateTag(doc) || doc.name.startsWith('_') || doc.name.startsWith('#')) {
            return false;
        } else if (doc instanceof MemberDoc) {
            return !this.isInternalMember(doc);
        } else if (doc instanceof ClassExportDoc) {
            doc.members = doc.members.filter(memberDoc => this.isPublicDoc(memberDoc));
        }
        return true;
    }

    /** Whether the given method member is listed as an internal member. */
    private isInternalMember(memberDoc: MemberDoc) {
        return INTERNAL_METHODS.includes(memberDoc.name);
    }

    /** Whether the given doc has a @docs-private tag set. */
    private hasDocsPrivateTag(doc: any) {
        const tags = doc.tags?.tags;
        return tags ? tags.find((d: any) => d.tagName === 'docs-private') : false;
    }
}
