import { Directive, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { AuthService } from '../../shared/services/auth.service';
import { PermissionService } from '../services/permission.service';
import { PermissionAction, PermissionRequirement } from '../models/permission.model';

/**
 * Structural directive that renders its content only if the current user has the
 * required permission. Re-evaluates when the current user changes (e.g. after a
 * matrix edit + refresh). Usage:
 *
 *   <button *appHasPermission="{ module: 'Expenses', action: 'write' }">Edit</button>
 *   <button *appHasPermission="['Expenses', 'approve']">Approve</button>
 */
@Directive({
    selector: '[appHasPermission]',
    standalone: true,
})
export class HasPermissionDirective implements OnInit, OnDestroy {
    private requirement: PermissionRequirement | null = null;
    private hasView = false;
    private destroy$ = new Subject<void>();

    constructor(
        private templateRef: TemplateRef<unknown>,
        private viewContainer: ViewContainerRef,
        private permissions: PermissionService,
        private auth: AuthService
    ) { }

    @Input()
    set appHasPermission(value: PermissionRequirement | [string, PermissionAction]) {
        if (Array.isArray(value)) {
            this.requirement = { module: value[0], action: value[1] };
        } else {
            this.requirement = value;
        }
        this.updateView();
    }

    ngOnInit(): void {
        // React to login/logout/refresh so visibility stays in sync with permissions.
        this.auth.currentUser$
            .pipe(takeUntil(this.destroy$))
            .subscribe(() => this.updateView());
    }

    ngOnDestroy(): void {
        this.destroy$.next();
        this.destroy$.complete();
    }

    private updateView(): void {
        const allowed = this.requirement
            ? this.permissions.can(this.requirement.module, this.requirement.action)
            : false;

        if (allowed && !this.hasView) {
            this.viewContainer.createEmbeddedView(this.templateRef);
            this.hasView = true;
        } else if (!allowed && this.hasView) {
            this.viewContainer.clear();
            this.hasView = false;
        }
    }
}
