import { Injectable } from '@angular/core';
import { GenericService } from '@iqser/common-ui';
import { catchError, map, tap } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { HttpHeaders } from '@angular/common/http';
import { saveAs } from 'file-saver';
import { ComponentDetails, ComponentLogEntry, Dictionary, IComponentLogData, IComponentLogEntry, IFile } from '@red/domain';
import { mapEach } from '@common-ui/utils';
import { FilePreviewStateService } from '../../modules/file-preview/services/file-preview-state.service';

@Injectable({ providedIn: 'root' })
export class ComponentLogService extends GenericService<void> {
    protected readonly _defaultModelPath = '';

    #componentLogRequest(
        dossierTemplateId: string,
        dossierId: string,
        fileId: string,
        includeDetails = true,
    ): Observable<IComponentLogData> {
        return this._http.get<IComponentLogData>(
            `/api/dossier-templates/${dossierTemplateId}/dossiers/${dossierId}/files/${fileId}/components`,
            {
                params: { includeDetails },
            },
        );
    }

    #bulkComponentLogRequest(dossierTemplateId: string, dossierId: string, includeDetails = false): Observable<IComponentLogData> {
        return this._http.get<IComponentLogData>(
            `/api/dossier-templates/${dossierTemplateId}/dossiers/${dossierId}/files/bulk/get-components`,
            {
                params: { includeDetails },
            },
        );
    }

    getComponentLogData(
        dossierTemplateId: string,
        dossierId: string,
        fileId: string,
        dictionaries: Dictionary[],
    ): Observable<ComponentLogEntry[]> {
        return this.#componentLogRequest(dossierTemplateId, dossierId, fileId).pipe(
            map(data => data.componentDetails),
            catchError(() => of({} as ComponentDetails)),
            map(componentDetails => this.#mapComponentDetails(componentDetails)),
            mapEach(log => new ComponentLogEntry(log)),
            map(log => this.#updateDisplayValue(log, dictionaries)),
        );
    }

    override(dossierTemplateId: string, dossierId: string, fileId: string, componentLogEntry: IComponentLogEntry) {
        return this._http.post(
            `/api/dossier-templates/${dossierTemplateId}/dossiers/${dossierId}/files/${fileId}/overrides`,
            componentLogEntry,
        );
    }

    revertOverride(dossierTemplateId: string, dossierId: string, fileId: string, components: string[]) {
        return this._http.post(`/api/dossier-templates/${dossierTemplateId}/dossiers/${dossierId}/files/${fileId}/overrides/revert`, {
            components,
        });
    }

    exportJSON(dossierTemplateId: string, dossierId: string, file?: IFile): Observable<IComponentLogData> {
        const request$ = file?.fileId
            ? this.#componentLogRequest(dossierTemplateId, dossierId, file?.fileId, false)
            : this.#bulkComponentLogRequest(dossierTemplateId, dossierId);
        return request$.pipe(
            tap(data => {
                const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
                saveAs(blob, (file?.filename ? `${file.filename}.` : '') + 'component_log.json');
            }),
        );
    }

    exportXML(dossierTemplateId, dossierId: string, file?: IFile): Observable<string> {
        return this.#getComponentLogDataAsXML(dossierTemplateId, dossierId, file).pipe(
            tap(data => {
                const blob = new Blob([data], { type: 'application/xml' });
                saveAs(blob, (file?.filename ? `${file.filename}.` : '') + 'component_log.xml');
            }),
        );
    }

    #getComponentLogDataAsXML(dossierTemplateId: string, dossierId: string, file?: IFile) {
        let headers = new HttpHeaders();
        headers = headers.set('accept', 'application/xml');

        const pathSuffix = file?.fileId ? `${file.fileId}/components` : 'bulk/get-components';

        return this._http.get(`/api/dossier-templates/${dossierTemplateId}/dossiers/${dossierId}/files/${pathSuffix}`, {
            headers: headers,
            responseType: 'text',
            observe: 'body',
            params: {
                includeDetails: false,
            },
        });
    }

    #mapComponentDetails(componentDetails: ComponentDetails): IComponentLogEntry[] {
        return Object.keys(componentDetails).reduce((res, key) => (res.push(componentDetails[key]), res), []);
    }

    #updateDisplayValue(componentLogs: ComponentLogEntry[], dictionaries: Dictionary[]): ComponentLogEntry[] {
        for (const componentLog of componentLogs) {
            for (const componentValue of componentLog.componentValues) {
                for (const reference of componentValue.entityReferences) {
                    const foundDictionary =
                        dictionaries.find(dict => dict.type === reference.type) ?? ({ label: reference.type } as Dictionary);
                    reference.displayValue = foundDictionary.label;
                }
            }
        }
        return componentLogs;
    }
}
