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';

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

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

    const fields = query.fields;
    return this.metaService.Flat.pipe(
      map((items: Header[]) =>
        items
          .filter((item: Header) => fields.includes(item.source))
          .sort(
            (a: Header, b: Header) =>
              fields.indexOf(a.source) - fields.indexOf(b.source)
          )
      ),
      take(1)
    );
  }

  data(query: Query): Observable<Record<string, any>[]> {
    if (!query?.isValid()) return of([]);

    const fields = query.fields;

    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
      map((data: Record<string, any>[]) =>
        data.map((i: Record<string, any>) => {
          const r = {} as Record<string, any>;
          fields.forEach((field: string) => (r[field] = i[field]));
          return r;
        })
      ),
      // make the data unique and apply paging
      map((data: Record<string, any>[]) => {
        let i = 0;
        const result: Record<string, any>[] = [];
        const page = query.page;

        data.some((d: Record<string, any>) => {
          const idx = result.findIndex((r: Record<string, any>) =>
            fields
              .map((field: string) => d[field] === r[field])
              .reduce((acc, cur) => acc && cur, true)
          );

          // if this element has not been seen before, add it to the results we care about.
          if (idx < 0) {
            result.push(d);
            ++i;
          }
          return i >= page.start + page.limit;
        });

        return result.slice(page.start, page.start + page.limit);
      }),
      // force it to close
      take(1)
    );
  }
}
