import {AfterViewInit, Component, Input, OnDestroy, OnInit, Type, ViewChild} from '@angular/core';
import {MatSort, Sort} from "@angular/material/sort";
import {PagingComponent} from "@app/_components/paging/paging.component";
import {MatTableDataSource} from "@angular/material/table";
import {ListColDefinition} from "@app/_components/list/list-col-definition";
import {AppHelperService} from "@app/_helpers/app-helper.service";
import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type";
import {ListUpdateElementFunctionType} from "@app/_components/list/list-update-element-function-type";
import {FilterBarComponent} from "@app/_components/filter-bar/filter-bar.component";
import { Router } from '@angular/router';
import {interval, Subscription} from "rxjs";
import {AbstractDataFormComponent} from "@app/_components/_abstract/abstract-data-form-component";

@Component({
    selector: 'app-list',
    templateUrl: './list.component.html',
    styleUrl: './list.component.scss'
})

export class ListComponent implements OnInit, AfterViewInit, OnDestroy {
    @Input() public listId!: string;
    @Input() public getDataFunction!: ListGetDataFunctionType;
    @Input() public onSortFunction?: Function;
    @Input() public onNavigateToDetailsFunction!: Function;
    @Input() public getCustomDetailLinkFunction!: Function;
    @Input() public onRemoveItemFunction!: Function;
    @Input() public onEditFunction!: Function;
    @Input() public onDownloadFunction!: Function;
    @Input() public onRowSelectedFunction!: Function;
    @Input() public onUpdateBooleanStateFunction!: ListUpdateElementFunctionType;
    @Input() public dataFormComponent!: Type<AbstractDataFormComponent<any>>;
    @Input() public dataFormComponentData?: any;
    @Input() public searchable: boolean;
    @Input() public showDetailButton: boolean;
    @Input() public showPosition: boolean;
    @Input() public showFilterBar: boolean;
    @Input() public listColDefinitions!: ListColDefinition[];
    @Input() public hidePageSize: boolean;
    @Input() public displayOptions!: { [key: string]: string };
    @Input() public defaultDisplayOption!: string;
    @Input() public refreshIntervalSeconds?: number;
    @Input() public detailLinkNewTab?: boolean;
    @ViewChild(MatSort) sort;
    @ViewChild("pagingComponent", {static: false}) protected pagingComponent!: PagingComponent;
    @ViewChild("filterBarComponent", {static: false}) protected filterBarComponent!: FilterBarComponent;

    public static COLUMN_TYPE_ADDRESS: string = 'address';
    public static COLUMN_TYPE_BOOLEAN: string = 'boolean';
    public static COLUMN_TYPE_BTN_DOWNLOAD: string = 'btn_download';
    public static COLUMN_TYPE_BTN_EDIT: string = 'btn_edit';
    public static COLUMN_TYPE_BTN_REMOVE: string = 'btn_remove';
    public static COLUMN_TYPE_CURRENCY: string = 'euro';
    public static COLUMN_TYPE_DATE: string = 'date';
    public static COLUMN_TYPE_DETAIL: string = 'detail';
    public static COLUMN_TYPE_DETAIL_LINK: string = 'detail_link';
    public static COLUMN_TYPE_EMAIL: string = 'email';
    public static COLUMN_TYPE_IMAGE: string = 'image';
    public static COLUMN_TYPE_COMBINED_IMAGES: string = 'combined_images';
    public static COLUMN_TYPE_NUMBER: string = 'number';
    public static COLUMN_TYPE_NUMBER_UNFORMATTED: string = 'number_unformatted';
    public static COLUMN_TYPE_NUMBER_BOLD: string = 'number_bold';
    public static COLUMN_TYPE_POSITION: string = 'position';
    public static COLUMN_TYPE_TEXT: string = 'text';
    public static COLUMN_TYPE_TEXT_BOLD: string = 'text_bold';
    public static COLUMN_TYPE_TEXT_LINKED: string = 'text_linked';
    public static COLUMN_TYPE_WEBSITE: string = 'website';

    public activeFilterCount: number = 0;

    protected displayedColumns!: string[];
    protected selectedRowIndex: number | null = null;
    protected dataSource;
    protected currentGroup!: string;
    protected filterExists!: boolean;
    protected sortObj!: any;
    protected filterObj!: any;
    protected listColDefinitionsByField!: any;
    protected filterConfig: string | null;
    private refreshSubscription?: Subscription;

    constructor(
        protected appHelperService: AppHelperService,
        private router: Router,
    ) {
        this.searchable = true;
        this.showDetailButton = true;
        this.showPosition = true;
        this.showFilterBar = true;
        this.filterExists = false;
        this.filterObj = {};
        this.sort = new MatSort();
        this.hidePageSize = false;
        this.dataSource = new MatTableDataSource<any>();
        this.filterConfig = null;
        this.detailLinkNewTab = false;
    }

    ngOnInit(): void {
        this.loadColumnConfig();
        if (this.showPosition) {
            this.listColDefinitions.unshift(ListComponent.getDefaultColPosition());
        }
        if (this.showDetailButton) {
            const url = this.getCustomDetailLinkFunction !== undefined ? '' : this.router.routerState.snapshot.url;
            this.listColDefinitions.unshift(ListComponent.getDefaultColDetailBtnLink(url));
        }
        if (this.displayOptions !== undefined) {
            this.currentGroup = this.defaultDisplayOption || Object.keys(this.displayOptions)[0] || '';
        }

        this.listColDefinitionsByField = {};
        this.listColDefinitions.forEach((value, index) => {
            if (value.visible === undefined) {
                value.visible = true;
            }
            this.listColDefinitionsByField[value['name']] = value;
            if (value.filterType !== undefined) {
                this.filterExists = true;
            }
        })
        this.updateDisplayedColumns();
        this.filterConfig = this.loadFilterConfig();
        this.setupAutoRefresh();
    }

    ngAfterViewInit(): void {
    }

    private setupAutoRefresh(): void {
        this.clearAutoRefresh();
        if (this.refreshIntervalSeconds && this.refreshIntervalSeconds > 0) {
            this.refreshSubscription = interval(this.refreshIntervalSeconds * 1000).subscribe(() => {
                this.getData();
            });
        }
    }

    private clearAutoRefresh(): void {
        if (this.refreshSubscription) {
            this.refreshSubscription.unsubscribe();
        }
    }

    saveFilterConfig(): void {
        localStorage.setItem(`filterConfig_${this.listId}`, this.getFilterJsonString());
    }

    loadFilterConfig(): string | null {
        return localStorage.getItem(`filterConfig_${this.listId}`);
    }

    saveColumnConfig(): void {
        const config = this.listColDefinitions.map(col => ({
            name: col.name,
            visible: col.visible
        }));
        localStorage.setItem(`listConfig_${this.listId}`, JSON.stringify(config));
    }

    loadColumnConfig(): void {
        const savedConfig = localStorage.getItem(`listConfig_${this.listId}`);
        if (savedConfig) {
            const config = JSON.parse(savedConfig);
            this.listColDefinitions.forEach(col => {
                const savedCol = config.find((c: any) => c.name === col.name);
                if (savedCol) {
                    col.visible = savedCol.visible;
                }
            });
            this.updateDisplayedColumns();
        }
    }

    updateDisplayedColumns(): void {
        this.displayedColumns = this.listColDefinitions
            .filter(col => col.visible !== false &&
                (this.displayOptions === undefined ||
                    col.groups?.includes(this.currentGroup) ||
                    col.type === ListComponent.COLUMN_TYPE_DETAIL ||
                    col.type === ListComponent.COLUMN_TYPE_POSITION))
            .map(col => col.name);
    }

    onDisplayOptionChange(option: string): void {
        this.currentGroup = option;
        this.updateDisplayedColumns();
    }

    onToggleColumnVisibility(columnName: string): void {
        const column = this.listColDefinitions.find(col => col.name === columnName);
        if (column) {
            column.visible = !column.visible;
            this.updateDisplayedColumns();
        }
    }

    showAllColumns() {
        this.listColDefinitions.forEach((value, index) => {
            value.visible = true;
        });
        this.updateDisplayedColumns();
    }

    getColumnVisibility(): { [key: string]: boolean } {
        const visibility: { [key: string]: boolean } = {};
        this.listColDefinitions.forEach(col => {
            visibility[col.name] = col.visible !== false;
        });
        return visibility;
    }

    getData = (): void => {
        this.getDataFunction(
            this.pagingComponent.getPageIndex(),
            this.pagingComponent.getPageSize(),
            this.pagingComponent.getSearchValue()
        ).subscribe(
            data => {
                this.dataSource = new MatTableDataSource<any>(data['member']);
                this.pagingComponent.setDataLength(data["totalItems"]);
            }
        )
    }

    onSortChange = (sortState: Sort) => {
        let listColDefinition: any = this.listColDefinitionsByField[sortState.active];
        this.sortObj = sortState;
        this.sortObj['listColDefinition'] = listColDefinition;
        this.pagingComponent.resetPageIndex();
        if (this.onSortFunction) {
            this.onSortFunction(sortState);
        }
        this.getData();
    }

    onRowSelected(row: any, index: number) {
        this.selectedRowIndex = index;
        if (this.onRowSelectedFunction !== undefined) {
            this.onRowSelectedFunction(row, index);
        }
    }

    getElementValue(element: any, column: ListColDefinition, multipleFieldIndex?: number): any | null {
        element = column.subResource !== undefined ? element[column.subResource] : element;
        if (element === undefined) {
            return null;
        }
        if (column.field !== undefined) {
            if (
                column.displayedLength !== undefined &&
                element[column.field] !== undefined &&
                element[column.field].length > column.displayedLength
            ) {
                return element[column.field]?.slice(0, column.displayedLength) + '...';
            }
            return element[column.field];
        }
        if (column.multipleFields !== undefined) {
            if (multipleFieldIndex !== undefined) {
                return element[column.multipleFields[multipleFieldIndex]];
            }
            let res: any[] = [];
            column.multipleFields.forEach((field, index) => {
                res.push(element[field]);
            })
            return res;
        }
        if (column.address !== undefined) {
            const field = column.address;
            let addressString = '';
            if (element[field.street] !== undefined && element[field.street] !== null) {
                addressString += `${element[field.street].trim()} `;
            }
            if (element[field.streetNo] !== undefined && element[field.streetNo] !== null) {
                addressString += `${element[field.streetNo].trim()} `;
            }
            addressString += ' <br/> ';
            if (element[field.zip] !== undefined && element[field.zip] !== null) {
                addressString += `${element[field.zip].trim()} `;
            }
            if (element[field.city] !== undefined && element[field.city] !== null) {
                addressString += `${element[field.city].trim()}`;
            }
            addressString += ' <br/> ';

            if (element[field.country] !== undefined && element[field.country] !== null) {
                addressString += `${element[field.country].trim()}`;
            }
            return addressString;
        }
        return element;
    }

    getElementImage(element: any, column: ListColDefinition): any {
        let elementValue = this.getElementValue(element, column);
        if (elementValue !== undefined && elementValue !== null) {
            return elementValue;
        }
        return "/assets/images/icons/dummy-person.png"
    }


    getColCssClass(column: ListColDefinition): string {
        switch (column.type) {
            case ListComponent.COLUMN_TYPE_DETAIL:
                return "spt-button-td";
            case ListComponent.COLUMN_TYPE_BTN_REMOVE:
                return "spt-button-td text-end";
            case ListComponent.COLUMN_TYPE_TEXT:
                return "spt-version-td";
            default:
                return "";
        }
    }

    public getPageIndex() {
        return this.pagingComponent.getPageIndex();
    }

    public getPageSize() {
        return this.pagingComponent.getPageSize();
    }

    public static getDefaultColDetailBtn(): ListColDefinition {
        return {
            name: 'detail',
            text: '',
            type: ListComponent.COLUMN_TYPE_DETAIL
        } as ListColDefinition;
    }

    public static getDefaultColDetailBtnLink(currentUrl: string): ListColDefinition {
        return {
            name: 'detaillink',
            text: '',
            url: currentUrl,
            type: ListComponent.COLUMN_TYPE_DETAIL_LINK
        } as ListColDefinition;
    }

    public static getDefaultColPosition(): ListColDefinition {
        return {
            name: 'pos',
            text: 'basic.number',
            type: ListComponent.COLUMN_TYPE_POSITION
        } as ListColDefinition;
    }

    public getSortingJsonString(): any
    {
        return JSON.stringify(this.sortObj);
    }

    public updateBooleanState = (element: any, index: number, column: ListColDefinition) => {
        if (this.onUpdateBooleanStateFunction === undefined) {
            throw new Error('no onUpdateBooleanStateFunction given');
        }
        if (column.field !== undefined) {
            element[column.field] = !element[column.field];
        } else {
            throw new Error('column.field is undefined');
        }
        this.onUpdateBooleanStateFunction(element).subscribe(
            data => {
                this.updateRow(data, index);
            }
        )
    }

    public updateRow(element: any, index: number) {
        const data = this.dataSource.data as any;
        data[index] = element;
        this.dataSource.data = data;
    }

    public onFilterInit(filterData: {filters: any, activeCount: number}) {
        this.filterObj = filterData.filters;
        this.activeFilterCount = filterData.activeCount;
    }

    public onFilterChanged(filterData: {filters: any, activeCount: number}) {
        console.log(filterData);
        const filterJson = JSON.stringify(filterData.filters);
        const currentFilterJson = JSON.stringify(this.filterObj);

        if (filterJson !== currentFilterJson) {
            this.filterObj = filterData.filters;
            this.activeFilterCount = filterData.activeCount;
            this.getData();
        }
    }

    public onCreateData() {
        this.appHelperService.openModal(
            this.dataFormComponent,
            this.dataFormComponentData,
            this.getData
        );
    }

    public getFilterJsonString(): any {
        return JSON.stringify(this.filterObj);
    }

    get COLUMN_TYPE_ADDRESS(): string {
        return ListComponent.COLUMN_TYPE_ADDRESS;
    }

    get COLUMN_TYPE_BOOLEAN(): string {
        return ListComponent.COLUMN_TYPE_BOOLEAN;
    }

    get COLUMN_TYPE_BTN_DOWNLOAD(): string {
        return ListComponent.COLUMN_TYPE_BTN_DOWNLOAD;
    }

    get COLUMN_TYPE_BTN_EDIT(): string {
        return ListComponent.COLUMN_TYPE_BTN_EDIT;
    }

    get COLUMN_TYPE_BTN_REMOVE(): string {
        return ListComponent.COLUMN_TYPE_BTN_REMOVE;
    }

    get COLUMN_TYPE_CURRENCY(): string {
        return ListComponent.COLUMN_TYPE_CURRENCY;
    }

    get COLUMN_TYPE_DATE(): string {
        return ListComponent.COLUMN_TYPE_DATE;
    }

    get COLUMN_TYPE_DETAIL(): string {
        return ListComponent.COLUMN_TYPE_DETAIL;
    }

    get COLUMN_TYPE_DETAIL_LINK(): string {
        return ListComponent.COLUMN_TYPE_DETAIL_LINK;
    }

    get COLUMN_TYPE_EMAIL(): string {
        return ListComponent.COLUMN_TYPE_EMAIL;
    }

    get COLUMN_TYPE_POSITION(): string {
        return ListComponent.COLUMN_TYPE_POSITION;
    }

    get COLUMN_TYPE_IMAGE(): string {
        return ListComponent.COLUMN_TYPE_IMAGE;
    }

    get COLUMN_TYPE_COMBINED_IMAGES(): string {
        return ListComponent.COLUMN_TYPE_COMBINED_IMAGES;
    }

    get COLUMN_TYPE_TEXT(): string {
        return ListComponent.COLUMN_TYPE_TEXT;
    }

    get COLUMN_TYPE_NUMBER(): string {
        return ListComponent.COLUMN_TYPE_NUMBER;
    }

    get COLUMN_TYPE_NUMBER_UNFORMATTED(): string {
        return ListComponent.COLUMN_TYPE_NUMBER_UNFORMATTED;
    }

    get COLUMN_TYPE_NUMBER_BOLD(): string {
        return ListComponent.COLUMN_TYPE_NUMBER_BOLD;
    }

    get COLUMN_TYPE_TEXT_BOLD(): string {
        return ListComponent.COLUMN_TYPE_TEXT_BOLD;
    }

    get COLUMN_TYPE_TEXT_LINKED(): string {
        return ListComponent.COLUMN_TYPE_TEXT_LINKED;
    }

    get COLUMN_TYPE_WEBSITE(): string {
        return ListComponent.COLUMN_TYPE_WEBSITE;
    }

    ngOnDestroy(): void {
        this.clearAutoRefresh();
    }
}