import { Component, inject, OnDestroy, OnInit } from '@angular/core';
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {
    CircleButtonComponent,
    HumanizePipe,
    IqserListingModule,
    IqserPermissionsService,
    ListingComponent,
    listingProvidersFactory,
    LoadingService,
    TableColumnConfig,
} from '@iqser/common-ui';
import { getCurrentUser, InitialsAvatarComponent } from '@iqser/common-ui/lib/users';
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest, User } from '@red/domain';
import { RouterHistoryService } from '@services/router-history.service';
import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
import { Roles } from '@users/roles';
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
import dayjs, { Dayjs } from 'dayjs';
import { firstValueFrom } from 'rxjs';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { AuditService } from '../../services/audit.service';
import { TranslateModule } from '@ngx-translate/core';
import { PaginationComponent } from '@common-ui/pagination';
import { MatFormField, MatSuffix } from '@angular/material/form-field';
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatIcon } from '@angular/material/icon';
import { DatePipe } from '@shared/pipes/date.pipe';

const PAGE_SIZE = 50;

@Component({
    templateUrl: './audit-screen.component.html',
    styleUrls: ['./audit-screen.component.scss'],
    providers: listingProvidersFactory(AuditScreenComponent),
    standalone: true,
    imports: [
        IqserListingModule,
        TranslateModule,
        PaginationComponent,
        ReactiveFormsModule,
        MatFormField,
        MatOption,
        NgForOf,
        InitialsAvatarComponent,
        NgIf,
        MatIcon,
        MatSuffix,
        AsyncPipe,
        DatePipe,
        HumanizePipe,
        CircleButtonComponent,
        MatSelectTrigger,
        MatSelect,
        MatDatepickerModule,
    ],
})
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
    private _previousFrom: Dayjs;
    private _previousTo: Dayjs;
    readonly ALL_CATEGORIES = 'allCategories';
    readonly ALL_USERS = _('audit-screen.all-users');
    readonly translations = auditCategoriesTranslations;
    readonly form: UntypedFormGroup = this.#getForm();
    readonly routerHistoryService = inject(RouterHistoryService);
    readonly permissionsService = inject(IqserPermissionsService);
    readonly roles = Roles;
    readonly currentUser = getCurrentUser<User>();
    categories: string[] = [];
    userIds: Set<string>;
    logs: IAuditResponse;
    readonly tableColumnConfigs: TableColumnConfig<Audit>[] = [
        { label: _('audit-screen.table-col-names.message') },
        { label: _('audit-screen.table-col-names.date') },
        { label: _('audit-screen.table-col-names.user'), class: 'user-column' },
        { label: _('audit-screen.table-col-names.category') },
    ];
    readonly tableHeaderLabel = _('audit-screen.table-header.title');

    constructor(
        private readonly _formBuilder: UntypedFormBuilder,
        private readonly _loadingService: LoadingService,
        private readonly _auditService: AuditService,
        private readonly _dialogService: AdminDialogService,
    ) {
        super();
    }

    get totalPages(): number {
        if (!this.logs) {
            return 0;
        }
        return Math.ceil(this.logs.totalHits / PAGE_SIZE);
    }

    async pageChanged(page: number) {
        await this.#fetchData(page);
    }

    async ngOnInit() {
        this.sortingService.setSortingOption({ column: 'recordDate', order: 'desc' });
        await this.#fetchData();
    }

    async filterChange() {
        if (!this.#updateDateFilters(this.form.value)) {
            await this.#fetchData();
        }
    }

    openAuditDetails(log: Audit) {
        this._dialogService.openDialog('auditInfo', { auditEntry: log });
    }

    #getForm(): UntypedFormGroup {
        return this._formBuilder.group({
            category: [this.ALL_CATEGORIES],
            userId: [this.ALL_USERS],
            from: [],
            to: [],
        });
    }

    #updateDateFilters(value): boolean {
        if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
            return true;
        }

        this._previousFrom = this.form.get('from').value;
        this._previousTo = this.form.get('to').value;
        return false;
    }

    async #fetchData(page?: number) {
        this._loadingService.start();
        const promises = [];
        const category = this.form.get('category').value;
        const userId = this.form.get('userId').value;
        const from = this.form.get('from').value ? dayjs(this.form.get('from').value).startOf('day').toISOString() : null;
        const to = this.form.get('to').value ? dayjs(this.form.get('to').value).endOf('day').toISOString() : null;

        const logsRequestBody: IAuditSearchRequest = {
            pageSize: PAGE_SIZE,
            page: page,
            category: category === this.ALL_CATEGORIES ? undefined : category,
            userId: userId === this.ALL_USERS ? undefined : userId,
            from,
            to,
        };

        promises.push(firstValueFrom(this._auditService.getCategories()));
        promises.push(firstValueFrom(this._auditService.searchAuditLog(logsRequestBody)));

        const data = await Promise.all(promises);
        this.categories = data[0].map(c => c.category);
        this.categories.splice(0, 0, this.ALL_CATEGORIES);
        this.logs = data[1];
        const entities = this.logs.data.map((log: IAudit) => new Audit(log));
        this.entitiesService.setEntities(entities);
        this.userIds = new Set<string>([this.ALL_USERS]);
        for (const id of this.logs.data.map(log => log.userId).filter(uid => !!uid)) {
            this.userIds.add(id);
        }
        this._loadingService.stop();
    }
}
