import { Component, ElementRef, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {
    CircleButtonTypes,
    CustomError,
    ErrorService,
    IqserPermissionsService,
    ListingComponent,
    ListingModes,
    listingProvidersFactory,
    LoadingService,
    TableColumnConfig,
    TableComponent,
    WorkflowConfig,
} from '@iqser/common-ui';
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
import {
    Dossier,
    DOSSIER_ID,
    DossierAttributeConfig,
    DossierAttributeWithValue,
    File,
    IFileAttributeConfig,
    WorkflowFileStatus,
} from '@red/domain';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { FilesMapService } from '@services/files/files-map.service';
import { FilesService } from '@services/files/files.service';
import { PermissionsService } from '@services/permissions.service';
import { FileUploadModel } from '@upload-download/model/file-upload.model';
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
import { FileUploadService } from '@upload-download/services/file-upload.service';
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
import { Roles } from '@users/roles';
import { UserPreferenceService } from '@users/user-preference.service';
import { convertFiles, Files, handleFileDrop } from '@utils/index';
import { merge, Observable } from 'rxjs';
import { filter, skip, switchMap, tap } from 'rxjs/operators';
import { ConfigService } from '../config.service';
import { BulkActionsService } from '../services/bulk-actions.service';

@Component({
    templateUrl: './dossier-overview-screen.component.html',
    styleUrls: ['./dossier-overview-screen.component.scss'],
    providers: [...listingProvidersFactory(DossierOverviewScreenComponent), ConfigService, BulkActionsService, dossiersServiceProvider],
})
export class DossierOverviewScreenComponent extends ListingComponent<File> implements OnInit, OnAttach, OnDetach, OnDestroy {
    #dossier: Dossier;
    @ViewChild('needsWorkFilterTemplate', { read: TemplateRef, static: true })
    private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
    @ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
    @ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
    #fileAttributeConfigs: IFileAttributeConfig[];
    readonly listingModes = ListingModes;
    readonly circleButtonTypes = CircleButtonTypes;
    readonly tableHeaderLabel = _('dossier-overview.table-header.title');
    collapsedDetails = false;
    dossierAttributes: DossierAttributeWithValue[] = [];
    tableColumnConfigs: readonly TableColumnConfig<File>[];
    displayedInFileListAttributes: IFileAttributeConfig[] = [];
    displayedAttributes: IFileAttributeConfig[] = [];
    displayedWorkflowAttributes: IFileAttributeConfig[] = [];
    readonly listingMode$ = this.configService.listingMode$.pipe(
        tap(() => this.#computeAllFilters()),
        shareLast(),
    );
    readonly dossier$: Observable<Dossier>;
    readonly files$: Observable<File[]>;
    readonly dossierId = getParam(DOSSIER_ID);
    readonly dossierTemplateId: string;
    readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus>;
    readonly dossierAttributes$: Observable<DossierAttributeConfig[]>;

    constructor(
        readonly configService: ConfigService,
        private readonly _errorService: ErrorService,
        private readonly _filesService: FilesService,
        readonly permissionsService: PermissionsService,
        iqserPermissionsService: IqserPermissionsService,
        private readonly _loadingService: LoadingService,
        private readonly _fileMapService: FilesMapService,
        private readonly _dossiersService: DossiersService,
        private readonly _fileUploadService: FileUploadService,
        private readonly _statusOverlayService: StatusOverlayService,
        private readonly _fileAttributesService: FileAttributesService,
        private readonly _userPreferenceService: UserPreferenceService,
        private readonly _fileDropOverlayService: FileDropOverlayService,
        private readonly _dossierTemplatesService: DossierTemplatesService,
        private readonly _dossierAttributesService: DossierAttributesService,
    ) {
        super();
        this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId).pipe(tap(dossier => (this.#dossier = dossier)));
        this.dossierAttributes$ = this._dossierAttributesService.all$.pipe(tap(() => this.#updateDossierAttributes()));
        this.#dossier = _dossiersService.find(this.dossierId);
        this.dossierTemplateId = this.#dossier.dossierTemplateId;
        const hasRss = iqserPermissionsService.has(Roles.getRss);
        this.workflowConfig = hasRss ? configService.workflowConfigRss() : configService.workflowConfig();
        this.files$ = merge(this.#files$, this.#dossierFilesChange$).pipe(shareLast());
        this.#updateFileAttributes();
    }

    get checkedRequiredFilters(): NestedFilter[] {
        return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
    }

    get checkedNotRequiredFilters(): NestedFilter[] {
        return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
    }

    get #files$() {
        return this._fileMapService.get$(this.dossierId).pipe(
            tap(files => this.entitiesService.setEntities(files)),
            tap(() => this.#computeAllFilters()),
        );
    }

    get #dossierFilesChange$() {
        return this._dossiersService.dossierFileChanges$.pipe(
            filter(dossierId => dossierId === this.dossierId),
            switchMap(dossierId => this._filesService.loadAll(dossierId)),
        );
    }

    disabledFn = (file: File) => file.excluded;

    lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;

    async ngOnInit(): Promise<void> {
        this.#computeAllFilters();
        this.#setRemovableSubscriptions();
        this.#initFileDropHandling();

        this._loadingService.stop();
    }

    ngOnAttach() {
        this.#initFileDropHandling();
        this.#setRemovableSubscriptions();
        this.#updateFileAttributes();
        this._tableComponent?.scrollToLastIndex();
    }

    override ngOnDetach() {
        this.#cleanupFileDropHandling();
        super.ngOnDetach();
    }

    override ngOnDestroy() {
        this.#cleanupFileDropHandling();
        super.ngOnDestroy();
    }

    @HostListener('drop', ['$event'])
    onDrop(event: DragEvent): void {
        if (this.permissionsService.canUploadFiles(this.#dossier)) {
            handleFileDrop(event, this.#dossier, this.#uploadFiles.bind(this));
        }
    }

    @HostListener('dragover', ['$event'])
    onDragOver(event): void {
        event.stopPropagation();
        event.preventDefault();
    }

    async uploadFiles(files: Files): Promise<void> {
        await this.#uploadFiles(convertFiles(files, this.#dossier));
        (this._fileInput as any).nativeElement.value = null;
    }

    #cleanupFileDropHandling() {
        if (this.permissionsService.canUploadFiles(this.#dossier)) {
            this._fileDropOverlayService.cleanupFileDropHandling();
        }
    }

    #initFileDropHandling(): void {
        if (this.permissionsService.canUploadFiles(this.#dossier)) {
            this._fileDropOverlayService.initFileDropHandling(this.dossierId);
        }
    }

    async #updateDossierAttributes(): Promise<void> {
        try {
            this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.#dossier);
        } catch (e) {
            console.error('[DOSSIER ATTRIBUTES] Error: ', e);
        }
    }

    #setRemovableSubscriptions(): void {
        this.addActiveScreenSubscription = this._dossiersService
            .getEntityDeleted$(this.dossierId)
            .pipe(tap(() => this.#handleDeletedDossier()))
            .subscribe();

        this.addActiveScreenSubscription = this._dossierTemplatesService
            .getEntityChanged$(this.#dossier.dossierTemplateId)
            .pipe(
                skip(1),
                tap(() => this.#updateFileAttributes()),
            )
            .subscribe();
    }

    #handleDeletedDossier(): void {
        this._errorService.set(
            new CustomError(_('error.deleted-entity.dossier.label'), _('error.deleted-entity.dossier.action'), 'iqser:expand'),
        );
    }

    #updateFileAttributes() {
        const attributes = this._fileAttributesService.getFileAttributeConfig(this.#dossier.dossierTemplateId);
        this.#fileAttributeConfigs = attributes?.fileAttributeConfigs || [];
        this.displayedInFileListAttributes = this.#fileAttributeConfigs.filter(config => config.displayedInFileList);
        this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
        this.displayedWorkflowAttributes = this.#getDisplayedWorkflowAttributes(this.displayedAttributes);
        this.tableColumnConfigs = this.configService.tableConfig(this.displayedAttributes);
        this.#computeAllFilters();
    }

    #getDisplayedWorkflowAttributes(displayedAttributes: IFileAttributeConfig[]): IFileAttributeConfig[] {
        let primaryAttribute = displayedAttributes.find(c => c.primaryAttribute);
        if (!primaryAttribute) {
            primaryAttribute = this.#fileAttributeConfigs.find(config => config.primaryAttribute);
            return primaryAttribute ? [primaryAttribute, ...displayedAttributes] : displayedAttributes;
        }
        return displayedAttributes.sort((c1, c2) => (c1.primaryAttribute ? -1 : c2.primaryAttribute ? 1 : 0));
    }

    async #uploadFiles(files: FileUploadModel[]) {
        const fileCount = await this._fileUploadService.uploadFiles(files, this.dossierId);
        if (fileCount) {
            this._statusOverlayService.openUploadStatusOverlay();
        }
    }

    #computeAllFilters() {
        const filterGroups = this.configService.filterGroups(
            this.entitiesService.all,
            this.#fileAttributeConfigs,
            this.#dossier.dossierTemplateId,
            this._needsWorkFilterTemplate,
            () => this.checkedRequiredFilters,
            () => this.checkedNotRequiredFilters,
        );
        this.filterService.addFilterGroups(filterGroups, true);
    }
}
