import { Injectable, PipeTransform, TemplateRef } from '@angular/core';

import { BehaviorSubject, Observable, of, Subject } from 'rxjs';

import { ApiResponse, Device } from './device';
import { DEVICE} from './devices';
import { DecimalPipe } from '@angular/common';
import { catchError, debounceTime, delay, switchMap, tap } from 'rxjs/operators';
import { SortColumn, SortDirection } from './sortable.directive';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { BaseService } from 'src/app/shared/service/base.service';

interface SearchResult {
  device: Device[];
  total: number;
}

interface State {
  page: number;
  pageSize: number;
  searchTerm: string;
  sortColumn: SortColumn;
  sortDirection: SortDirection;
}

const compare = (v1: string | number, v2: string | number) => v1 < v2 ? -1 : v1 > v2 ? 1 : 0;

function sort(devices: Device[], column: SortColumn, direction: string): Device[] {
  if (direction === '' || column === '') {
    return devices;
  } else {
    return [...devices].sort((a, b) => {
      const res = compare(a[column], b[column]);
      return direction === 'asc' ? res : -res;
    });
  }
}

function matches(device: Device , term: string, pipe: PipeTransform) {
  return device.deviceName.toLowerCase().includes(term.toLowerCase())
}

@Injectable({ providedIn: 'root' })
export class DeviceService {
  private _loading$ = new BehaviorSubject<boolean>(true);
  private _search$ = new Subject<void>();
  private _devices$ = new BehaviorSubject<Device[]>([]);
  private _total$ = new BehaviorSubject<number>(0);
  private modalRef: NgbModalRef;
  private addDeviceTemplate: TemplateRef<any>;
  private templateSetPromise: Promise<void>;
  private templateSetResolve: () => void;


  private _state: State = {
    page: 1,
    pageSize: 10,
    searchTerm: '',
    sortColumn: '',
    sortDirection: ''
  };

  constructor(private pipe: DecimalPipe,private modalService: NgbModal,private baseService:BaseService) {
    this.templateSetPromise = new Promise<void>((resolve) => {
      this.templateSetResolve = resolve;
    });
    this._search$.pipe(
      tap(() => this._loading$.next(true)),
      debounceTime(200),
      switchMap(() => this._search()),
      delay(200),
      tap(() => this._loading$.next(false))
    ).subscribe(result => {
      this._devices$.next(result.device);
      this._total$.next(result.total);
    });

    this._search$.next();
  }

  get device$() { return this._devices$.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 searchTerm() { return this._state.searchTerm; }

  set page(page: number) { this._set({ page }); }
  set pageSize(pageSize: number) { this._set({ pageSize }); }
  set searchTerm(searchTerm: string) { this._set({ searchTerm }); }
  set sortColumn(sortColumn: SortColumn) { this._set({ sortColumn }); }
  set sortDirection(sortDirection: SortDirection) { this._set({ sortDirection }); }
  public ids: string[] = [];

  private _set(patch: Partial<State>) {
    Object.assign(this._state, patch);
    this._search$.next();
  }
  // openAddDeviceModal(templateRef: TemplateRef<any>) {
  //   this.modalRef = this.modalService.open(templateRef,  { scrollable: true ,windowClass:'modal-holder', centered: true, backdrop: "static"});
  // }
  setAddDeviceTemplate(template: TemplateRef<any>) {
    this.addDeviceTemplate = template;
    this.templateSetResolve();
  }

  fetchDevices(): void {
    this._loading$.next(true);
    this.baseService.getData('devices/getAllDevices').pipe(
      tap((response: any) => {
        if (response && response.status === 'Success') {
          this._devices$.next(response.data);
          this._total$.next(response.data.length);
        } else {
          console.error('Failed to fetch devices:', response.message);
        }
      }),
      catchError(error => {
        console.error('An error occurred while fetching devices:', error);
        return of([]);
      }),
      tap(() => this._loading$.next(false))
    ).subscribe();
  }

  openAddDeviceModal() {
    this.templateSetPromise.then(() => {
      if (this.addDeviceTemplate) {
        this.modalRef = this.modalService.open(this.addDeviceTemplate, { scrollable: true });
      } else {
        console.error('Add Device Modal template is not set.');
      }
    });
  }

  // openAddDeviceModal() {
  //   if (this.addDeviceTemplate) {
  //     this.modalRef = this.modalService.open(this.addDeviceTemplate, { scrollable: true });
  //   } else {
  //     console.error('Add Device Modal template is not set.');
  //   }
  // }
  closeAddDeviceModal() {
    if (this.modalRef) {
      this.modalRef.close();
    }
  }
  private generator(): string {
    const isString = `${this.S4()}${this.S4()}-${this.S4()}-${this.S4()}-${this.S4()}-${this.S4()}${this.S4()}${this.S4()}`;

    return isString;
  }
  private S4(): string {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  }
  generateUUID() {
    // return this.baseService.generate();;
    let isUnique = false;
    let tempId = '';

    while (!isUnique) {
      tempId = this.generator();
      if (!this.idExists(tempId)) {
        isUnique = true;
        this.ids.push(tempId);
      }
    }

    return tempId;
  }
  // setAddDeviceTemplate(template: TemplateRef<any>) {
  //   this.addDeviceTemplate = template;
  // }
   private idExists(id: string): boolean {
    return this.ids.includes(id);
  }
  private _search(): Observable<SearchResult> {
    const { sortColumn, sortDirection, pageSize, page, searchTerm } = this._state;

    // 1. sort
    let device = sort(DEVICE, sortColumn, sortDirection);

    // 2. filter
    device = device.filter(transaction => matches(transaction, searchTerm, this.pipe));
    const total = device.length;

    // 3. paginate
    device = device.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);
    return of({ device, total });
  }

  
}