import {Component, ViewChildren, QueryList, Input, OnInit, Output, EventEmitter} from '@angular/core';
import { BehaviorSubject } from "rxjs";
import {JSONSchema7} from "json-schema";
import { ActivatedRoute, Router } from '@angular/router';
import {CrudService, Filters, SortBy} from "../crud.service";
import { NgbdSortableHeader, SortColumn, SortDirection } from './sortable.directive';
import { JsonschemasService } from "../jsonschemas.service";


interface State {
    page: number;
    pageSize: number;
    searchTerm: string;
    sortColumn: SortColumn;
    sortDirection: SortDirection;
    searchFilters: {[key: string]: any}
}

interface Column {
    path: string,
    title: string
}

@Component({
    selector: 'crud-list',
    templateUrl: './list.component.html',
    styleUrls: ['./list.component.css'],
})
export class ListComponent implements OnInit {
    @Input() resource: string = "";
    @Input() columns: string[] = [];
    @Input() filters: string[] = [];
    @Input() schema: string | undefined;

    @Output() error: EventEmitter<string> = new EventEmitter();
    @Output() result: EventEmitter<string> = new EventEmitter();

    @ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader> = new QueryList<NgbdSortableHeader>();

    public displayedColumns: Column[] = [];

    private _loading$ = new BehaviorSubject<boolean>(true);
    private _listData$ = new BehaviorSubject<any[]>([]);
    private _total$ = new BehaviorSubject<number>(0);

    private _state: State = {
        page: 1,
        pageSize: 15,
        searchTerm: '',
        sortColumn: '_id',
        sortDirection: 'asc',
        searchFilters: {}
    };

    constructor(private service: CrudService,
                private jsonSchemasService: JsonschemasService,
                private router: Router,
                private route: ActivatedRoute,
    ) { }

    ngOnInit(): void {
        this.jsonSchemasService.getUpdateResource(this.schema!).subscribe({
            next: (schema: any) => this.getColumnDefinition(schema),
            error: (err) => this.error.emit("Error loading the schema:" + err)
        });
        this.route.queryParams.subscribe(params => {
            let parsedParams = {...params};
            if (parsedParams.hasOwnProperty('searchFilters')) {
                parsedParams['searchFilters'] = JSON.parse(parsedParams['searchFilters']);
            }
            this._total$.next(this.page * this.pageSize);
            this._set(parsedParams)
        });
    }

    getColumnDefinition(schema: JSONSchema7) {
        for (let column of this.columns) {
            if (this.jsonSchemasService.path_exists(schema, column)) {
                this.displayedColumns.push({
                    path: column,
                    title: this.jsonSchemasService.get_property_by_path(schema, column).title!
                });
            }
        }

        if (this.displayedColumns.length == 0) {
            for (let param_name in schema.properties) {
                if (param_name != "_id") {
                    this.displayedColumns.push({
                        path: param_name,
                        title: this.jsonSchemasService.get_property_by_path(schema, param_name).title!
                    });
                }
            }
        }
    }

    getColumnValue(row: any, col: string): string {
        let parent = row;
        for (const key of col.split('.')) {
            if (key == 'items' && Array.isArray(parent)) {
                let path_parts = col.split(/items\.(.*)/s);
                let subkey = path_parts[1]
                return parent.map((v: any) => this.getColumnValue(v, subkey)).join(', ');
            } else {
                parent = parent[key];
            }
        }
        return parent.replace(/<[^>]*>/g, '');
    }

    private _search() {
        this._loading$.next(true);
        let sortBy = new SortBy(this.sortColumn, this.sortDirection)
        let filters = this.searchTerm ? [new Filters('fulltext', 'eq', this.searchTerm)] : [];
        for (let f in this.searchFilters) {
            if (Array.isArray(this.searchFilters[f])) {
                filters.push(new Filters(f, 'in', this.searchFilters[f]))
            } else {
                filters.push(new Filters(f, 'eq', this.searchFilters[f]))
            }
        }

        this.service.getList(this.resource, this.page, this.pageSize, [sortBy], filters).subscribe({
            next: (data: any) => {
                this._listData$.next(data.items);
                this._total$.next(data.total);
                this._state.pageSize = data.size;
                this._state.page = data.page;
                this._loading$.next(false);
                this.result.emit(`Found ${data.total} records`)
            },
            error: (err) => this.error.emit("Error loading the list:" + err)
        });
    }

    onFilterChange(event: any) {
        this.searchFilters = event;
    }

  onSort({ column, direction }: any) {
      // resetting other headers
      this.headers.forEach((header) => {
          if (header.sortable !== column) {
              header.direction = '';
          }
      });

      this.sortColumn = column;
      this.sortDirection = direction;
  }

  onRowClick(id: string) {
      this.router.navigate([`../${id}`], {relativeTo: this.route});
  }

  onRowMiddleClick(id: string) {
      let newUrl = window.location.href.replace('list', id).split('?')[0]
      window.open(newUrl, '_blank');
  }

  onCreate() {
      this.router.navigate([`../new`], {relativeTo: this.route});
  }

  get listData$() {
      return this._listData$.asObservable();
  }
  get total$() {
      return this._total$.asObservable();
  }
  get loading$() {
      return this._loading$.asObservable();
  }
  get page() {
      return this._state.page;
  }
  get pageSize() {
      return this._state.pageSize;
  }
  get sortColumn() {
      return this._state.sortColumn;
  }
  get sortDirection() {
      return this._state.sortDirection;
  }
  get searchTerm() {
      return this._state.searchTerm;
  }
  get searchFilters() {
      return this._state.searchFilters;
  }

    set page(page: number) {
        this.updateState({ page });
    }
    
    set pageSize(pageSize: number) {
        this.updateState({ pageSize });
    }
    
    set searchTerm(searchTerm: string) {
        this.updateState({ searchTerm });
    }

    set searchFilters(searchFilters: {[key: string]: any}) {
        this.updateState({ searchFilters: JSON.stringify(searchFilters) });
    }

    set sortColumn(sortColumn: SortColumn) {
        this.updateState({ sortColumn });
    }

    set sortDirection(sortDirection: SortDirection) {
        this.updateState({ sortDirection });
    }

    private updateState(patch: any) {
        this.router.navigate([], {
            relativeTo: this.route,
            queryParams: patch ,
            queryParamsHandling: 'merge'
        });
    }

    private _set(patch: Partial<State>) {
        Object.assign(this._state, patch);
        this._search();
    }
}
