import {Component, ElementRef, isDevMode, OnInit, ViewChild} from '@angular/core';
import {ApiService} from '../services/api.service';
import {AutocompleteService} from '../services/autocomplete.service';
import _ from 'lodash';
import {SampleModel} from '../models/sample.model';


interface LoadSamplesOptions {
  toPage?: number;
  event?: Event;
  firstPage?: boolean;
}
interface KeyInterface {
  id: string;
  label: string;
  active: boolean;
  sortable: boolean;
}

@Component({
  selector: 'app-samples',
  templateUrl: './samples.component.html',
  styleUrls: ['./samples.component.scss']
})


// TODO: check if custom-header.conf works, add headers from helmet https://docs.cloudfoundry.org/buildpacks/staticfile/index.html

export class SamplesComponent implements OnInit {

  @ViewChild('pageSizeSelection') pageSizeSelection: ElementRef<HTMLElement>;
  @ViewChild('linkarea') linkarea: ElementRef<HTMLTextAreaElement>;

  downloadCsv = false;
  materials = {};
  samples: SampleModel[] = [];
  totalSamples = 0;  // total number of samples
  csvUrl = '';  // store url separate so it only has to be generated when clicking the download button
  filters = {
    status: {new: true, validated: true},
    pageSize: 25,
    toPage: 0,
    sort: 'added-asc',
    filters: [
      {field: 'number', label: 'Number', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.number', label: 'Material number', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.name', label: 'Material name', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.supplier', label: 'Supplier', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.group', label: 'Material', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.glass_fiber', label: 'GF', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.carbon_fiber', label: 'CF', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'material.mineral', label: 'M', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'type', label: 'Type', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'color', label: 'Color', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'batch', label: 'Batch', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'notes', label: 'Notes', active: false, autocomplete: [], mode: 'eq', values: ['']},
      {field: 'added', label: 'Added', active: false, autocomplete: [], mode: 'eq', values: [new Date()]}
    ]
  };
  page = 1;
  pages = 1;
  loadSamplesQueue = [];  // arguments of queued up loadSamples() calls
  apiKey = '';
  keys: KeyInterface[] = [
    {id: 'number', label: 'Number', active: true, sortable: true},
    {id: 'material.numbers', label: 'Material numbers', active: true, sortable: false},
    {id: 'material.name', label: 'Material name', active: true, sortable: true},
    {id: 'material.supplier', label: 'Supplier', active: true, sortable: true},
    {id: 'material.group', label: 'Material', active: false, sortable: true},
    {id: 'type', label: 'Type', active: true, sortable: true},
    {id: 'color', label: 'Color', active: true, sortable: true},
    {id: 'batch', label: 'Batch', active: true, sortable: true},
    {id: 'notes', label: 'Notes', active: false, sortable: false},
    {id: 'added', label: 'Added', active: true, sortable: true},
  ];
  isActiveKey: {[key: string]: boolean} = {};
  activeKeys: KeyInterface[] = [];
  activeTemplateKeys = {material: [], measurements: []};


  constructor(
    private api: ApiService,
    public autocomplete: AutocompleteService
  ) {
  }

  ngOnInit(): void {
    this.calcFieldSelectKeys();
    this.api.get('/materials?status=all', (mData: any) => {
      this.materials = {};
      mData.forEach(material => {
        this.materials[material._id] = material;
      });
      this.filters.filters.find(e => e.field === 'material.name').autocomplete = mData.map(e => e.name);
      this.filters.filters.find(e => e.field === 'color').autocomplete = [...new Set(mData.reduce((s, e) => {s.push(...e.numbers.map(el => el.color)); return s; }, []))];
      this.loadSamples();
    });
    this.api.get('/user/key', (data: {key: string}) => {
      this.apiKey = data.key;
    });
    this.api.get<string[]>('/material/suppliers', (data: any) => {
      this.filters.filters.find(e => e.field === 'material.supplier').autocomplete = data;
    });
    this.api.get<string[]>('/material/groups', (data: any) => {
      this.filters.filters.find(e => e.field === 'material.group').autocomplete = data;
    });
    this.loadTemplateKeys('materials', 'type');
    this.loadTemplateKeys('measurements', 'added');
  }

  loadTemplateKeys(collection, insertBefore) {
    this.api.get('/template/' + collection, (data: {name: string, parameters: {name: string, range: object}[]}[]) => {
      const templateKeys = [];
      data.forEach(item => {
        item.parameters.forEach(parameter => {
          const parameterName = encodeURIComponent(parameter.name);
          if (parameter.name !== 'dpt' && !templateKeys.find(e => new RegExp('.' + parameterName + '$').test(e.id))) {  // exclude spectrum
            templateKeys.push({id: `${collection === 'materials' ? 'material.properties' : collection + '.' + item.name}.${parameterName}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, sortable: true});
            this.filters.filters.push({field: `${collection === 'materials' ? 'material.properties' : collection + '.' + item.name}.${parameterName}`, label: `${this.ucFirst(item.name)} ${this.ucFirst(parameter.name)}`, active: false, autocomplete: [], mode: 'eq', values: ['']});
          }
        });
      });
      this.keys.splice(this.keys.findIndex(e => e.id === insertBefore), 0, ...templateKeys);
      this.keys = [...this.keys];  // complete overwrite array to invoke update in rb-multiselect
      this.updateActiveKeys();
      this.calcFieldSelectKeys();
    });
  }

  loadSamples(options: LoadSamplesOptions = {}, event = null) {  // set toPage to null to reload first page, queues calls
    if (event) {  // adjust active keys
      this.keys.forEach(key => {
        if (event.hasOwnProperty(key.id)) {
          key.active = event[key.id];
        }
      });
      this.updateActiveKeys();
    }
    this.loadSamplesQueue.push(options);
    if (this.loadSamplesQueue.length <= 1) {  // nothing queued up
      this.sampleLoader(this.loadSamplesQueue[0]);
    }
  }

  private sampleLoader(options: LoadSamplesOptions) {  // actual loading of the sample, do not call directly
    this.api.get(this.sampleUrl({paging: true, pagingOptions: options}), (sData, ignore, headers) => {
      if (!options.toPage && headers['x-total-items']) {
        this.totalSamples = headers['x-total-items'];
      }
      this.pages = Math.ceil(this.totalSamples / this.filters.pageSize);
      this.samples = sData as any;
      this.loadSamplesQueue.shift();
      if (this.loadSamplesQueue.length > 0) {  // execute next queue item
        this.sampleLoader(this.loadSamplesQueue[0]);
      }
    });
  }

  sampleUrl(options: {paging?: boolean, pagingOptions?: {firstPage?: boolean, toPage?: number, event?: Event}, csv?: boolean, export?: boolean, host?: boolean}) {  // return url to fetch samples
    const additionalTableKeys = ['material_id', '_id'];  // keys which should always be added if export = false
    const query: string[] = [];
    query.push('status=' + (this.filters.status.new && this.filters.status.validated ? 'all' : (this.filters.status.new ? 'new' : 'validated')));
    if (options.paging) {
      if (this.samples[0]) {  // do not include from-id when page size was changed
        if (!options.pagingOptions.firstPage) {
          query.push('from-id=' + this.samples[0]._id);
        }
        else {
          this.page = 1;
        }
      }
      if (options.pagingOptions.toPage) {
        query.push('to-page=' + options.pagingOptions.toPage);
      }
      query.push('page-size=' + this.filters.pageSize);
    }
    query.push('sort=' + this.filters.sort);
    if (options.csv) {
      query.push('csv=true');
    }
    if (options.export) {
      query.push('key=' + this.apiKey);
    }
    this.keys.forEach(key => {
      if (key.active && (options.export || (!options.export && key.id.indexOf('material') < 0))) {  // do not load material properties for table
        query.push('fields[]=' + key.id);
      }
    });
    console.log(this.filters.filters);

    query.push(..._.cloneDeep(this.filters.filters)
      .map(e => {
        e.values = e.values.filter(el => el !== '');  // do not include empty values
        if (e.field === 'added') {  // correct timezone
          e.values = e.values.map(el => new Date(new Date(el).getTime() - new Date(el).getTimezoneOffset() * 60000).toISOString());
        }
        return e;
      })
      .filter(e => e.active && e.values.length > 0)
      .map(e => 'filters[]=' + encodeURIComponent(JSON.stringify(_.pick(e, ['mode', 'field', 'values']))))
    );
    console.log(this.filters);
    if (!options.export) {
      additionalTableKeys.forEach(key => {
      if (query.indexOf('fields[]=' + key) < 0) {  // add key if not already added
        query.push('fields[]=' + key);
      }
      });
    }
    else if (this.downloadCsv) {
      query.push('fields[]=measurements.spectrum.dpt');
    }
    console.log('/samples?' + query.join('&'));
    return (options.host && isDevMode() ? window.location.host : '') + (options.export ? this.api.hostName : '') + '/samples?' + query.join('&');
  }

  loadPage(delta) {
    if (!/[0-9]+/.test(delta) || (this.page <= 1 && delta < 0)) {  // invalid delta
      return;
    }
    this.page += delta;
    this.loadSamples({toPage: delta});
  }

  updateFilterFields(field) {
    const filter = this.filters.filters.find(e => e.field === field);
    filter.active = true;
    if (filter.mode === 'in' || filter.mode === 'nin') {
      if (filter.values[filter.values.length - 1] === '' && filter.values[filter.values.length - 2] === '') {
        filter.values.pop();
      }
      else if (filter.values[filter.values.length - 1] !== '') {
        filter.values.push((filter.field === 'added' ? new Date() : '') as string & Date);
      }
    }
    else {
      filter.values = [filter.values[0] as string & Date];
    }
    if (filter.active) {
      this.loadSamples({firstPage: true});
    }
  }

  setSort(string) {
    this.filters.sort = string;
    this.loadSamples({firstPage: true});
  }

  updateActiveKeys() {  // array with all activeKeys
    this.activeKeys = this.keys.filter(e => e.active);
    this.activeTemplateKeys.material = this.keys.filter(e => e.id.indexOf('material.properties.') >= 0 && e.active).map(e => e.id.split('.').map(el => decodeURIComponent(el)));
    this.activeTemplateKeys.measurements = this.keys.filter(e => e.id.indexOf('measurements.') >= 0 && e.active).map(e => e.id.split('.').map(el => decodeURIComponent(el)));
    console.log(this.activeTemplateKeys);
    console.log(this.keys);  // TODO: glass fiber filter not working
  }

  calcFieldSelectKeys() {
    console.log('CALC');
    console.log(this.keys);
    this.keys.forEach(key => {
      this.isActiveKey[key.id] = key.active;
    });
  }

  preventDefault(event, key = 'all') {
    if (key === 'all' || event.key === key) {
      event.preventDefault();
    }
  }

  clipboard() {
    this.linkarea.nativeElement.select();
    this.linkarea.nativeElement.setSelectionRange(0, 99999);
    document.execCommand('copy');
  }

  ucFirst(string) {
    return string[0].toUpperCase() + string.slice(1);
  }
}