import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable, NgZone } from '@angular/core';
import { Observable } from 'rxjs';

type Params = Record<string, string | number | boolean | null | undefined>;

@Injectable({ providedIn: 'root' })
export class ApiService {
  constructor(
    private readonly http: HttpClient,
    private readonly zone: NgZone
  ) {}

  login(username: string, password: string): Observable<any> {
    return this.inAngularZone(this.http.post('/api/auth/login', { username, password }, { withCredentials: true }));
  }

  me(): Observable<any> {
    return this.inAngularZone(this.http.get('/api/auth/me', { withCredentials: true }));
  }

  logout(): Observable<any> {
    return this.inAngularZone(this.http.post('/api/auth/logout', {}, { withCredentials: true }));
  }

  dashboard(): Observable<any> {
    return this.inAngularZone(this.http.get('/api/dashboard'));
  }

  tickets(params: Params): Observable<any> {
    return this.inAngularZone(this.http.get('/api/tickets', { params: this.toHttpParams(params) }));
  }

  ticket(ticketId: number): Observable<any> {
    return this.inAngularZone(this.http.get(`/api/tickets/${ticketId}`));
  }

  searchText(params: Params): Observable<any> {
    return this.inAngularZone(this.http.get('/api/search/text', { params: this.toHttpParams(params) }));
  }

  searchTicket(ticketId: number, params: Params): Observable<any> {
    return this.inAngularZone(this.http.get(`/api/search/ticket/${ticketId}`, { params: this.toHttpParams(params) }));
  }

  feedback(payload: any): Observable<any> {
    return this.inAngularZone(this.http.post('/api/feedback', payload));
  }

  users(params: Params): Observable<any> {
    return this.inAngularZone(this.http.get('/api/users', { params: this.toHttpParams(params) }));
  }

  updateUser(tid: number, patch: any): Observable<any> {
    return this.inAngularZone(this.http.patch(`/api/users/${tid}`, patch));
  }

  userFilters(tid: number): Observable<any> {
    return this.inAngularZone(this.http.get(`/api/users/${tid}/filters`));
  }

  addUserFilter(tid: number, payload: any): Observable<any> {
    return this.inAngularZone(this.http.post(`/api/users/${tid}/filters`, payload));
  }

  deleteUserFilter(tid: number, fid: number): Observable<any> {
    return this.inAngularZone(this.http.delete(`/api/users/${tid}/filters/${fid}`));
  }

  userSettings(tid: number): Observable<any> {
    return this.inAngularZone(this.http.get(`/api/users/${tid}/settings`));
  }

  updateUserSettings(tid: number, patch: any): Observable<any> {
    return this.inAngularZone(this.http.patch(`/api/users/${tid}/settings`, patch));
  }

  taxonomy(kind: string, params: Params = {}): Observable<any> {
    return this.inAngularZone(this.http.get(`/api/taxonomy/${kind}`, { params: this.toHttpParams(params) }));
  }

  private inAngularZone<T>(source: Observable<T>): Observable<T> {
    return new Observable<T>((observer) => {
      const subscription = source.subscribe({
        next: (value) => this.zone.run(() => observer.next(value)),
        error: (error) => this.zone.run(() => observer.error(error)),
        complete: () => this.zone.run(() => observer.complete())
      });
      return () => subscription.unsubscribe();
    });
  }

  private toHttpParams(params: Params): HttpParams {
    let httpParams = new HttpParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value === null || value === undefined || value === '') {
        return;
      }
      httpParams = httpParams.set(key, String(value));
    });
    return httpParams;
  }
}
