import { Injectable } from '@angular/core';
import { DataService } from './data.service';
import { Query } from '../models/query';
import { Observable, of, map, take } from 'rxjs';
import { Header } from '../models/header';
import { MetaService } from './meta.service';
import { SORT } from '../enums/sort';
import { Result } from '../models/result';

@Injectable({ providedIn: 'root' })
export class ExecuteService {
  constructor(
    private dataService: DataService,
    private metaService: MetaService
  ) {}

  headers(query: Query): Header[] {
    if (!query?.isValid?.()) return [];

    const fields = [...query.fields];
    const sort = query.sort;

    return this.metaService
      .Flat()
      .filter((item: Header) => fields.includes(item.source))
      .sort(
        (a: Header, b: Header) =>
          fields.indexOf(a.source) - fields.indexOf(b.source)
      )
      .map((item: Header) => {
        if (item.source === sort.field) {
          item.sort = sort.dir;
        }
        return item;
      });
  }

  data(query: Query): Observable<Result> {
    if (!query?.isValid()) return of(new Result({}));
    console.log(query.toString());

    const fields = [...query.fields];
    const page = query.page;
    const sort = query.sort;

    const headers = this.headers(query);

    return this.dataService.Data$.pipe(
      // apply filter
      map((data: Record<string, any>[]) => {
        const filter = query.filter;
        return data.filter((i: Record<string, any>) => {
          // apply filter to i to determine if it should be returned
          return true;
        });
      }),
      // apply fields, make unique
      map((data: Record<string, any>[]) => {
        let unique = new Map<string, Record<string, any>>();
        data.forEach((i: Record<string, any>) => {
          const r = {} as Record<string, any>;
          fields.forEach((field: string) => (r[field] = i[field]));
          const key = fields.map((field: string) => i[field]).join(':');
          unique.set(key, r);
        });
        return [...unique].map(([name, value]) => value);
      }),
      // sort
      map((data: Record<string, any>[]) => {
        if (!sort.isValid()) return data;
        const field = sort.field;

        if (!fields.includes(field)) {
        } else if (sort.dir === SORT.ASC) {
          return data.sort((a: Record<string, any>, b: Record<string, any>) =>
            a[field] > b[field] ? 1 : -1
          );
        } else if (sort.dir === SORT.DSC) {
          return data.sort((a: Record<string, any>, b: Record<string, any>) =>
            a[field] < b[field] ? 1 : -1
          );
        }
        return data;
      }),
      // paging
      map(
        (data: Record<string, any>[]) =>
          new Result({
            headers: headers,
            data: page
              ? data.slice((page.page - 1) * page.size, page.page * page.size)
              : data,
            page: page,
            total: data.length,
          })
          
      ),
      // force it to close
      take(1)
    );
  }
}
