import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import moment, { Moment } from 'moment';
import { environment } from 'src/environments/environment';
import { User } from '../models/user.model';
import { BehaviorSubject, from, Observable } from 'rxjs';
import {jwtDecode} from "jwt-decode";

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  webCredentials?: string;
  apiToken?: string;
  user?: User;
  apiTokenExpirationTime?: Moment;

  bc!: BroadcastChannel;

  constructor(
    private http$: HttpClient,
  ) {
    this.createBroadcastChannel();
    this.askForAuthData();
  }

  ping() {
    return new Promise((resolve, reject) => {
      this.http$.get<string>(`${environment.api.url}/ping/ping`).subscribe({
        next: (data) => {
          resolve(data);
        },
        error: (e) => {
          console.error('Ping error', e);
          reject(e);
        }
      }
      );
    });
  }
  retrieveUserFromCredentials(credentials: string) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const responsePayload: any = jwtDecode(credentials);
    this.user = new User({
      userName: `${responsePayload.given_name} ${responsePayload.family_name}`,
      email: responsePayload.email,
      avatar: responsePayload.picture
    });
  }
  getAPIToken(): Promise<void> {
    return new Promise((resolve, reject) => {
      const header = new HttpHeaders().set('Content-type', 'application/json');
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      this.http$.post<any>(`${environment.api.url}/auth/apiToken`, JSON.stringify(this.webCredentials), { headers: header }).subscribe({
        next: (data) => {
          this.user!.id = data.id;
          this.apiToken = data.token;
          this.apiTokenExpirationTime = moment(data.expirationTime);
          this.sendAuthData();
          resolve(data);
        },
        error: (e: HttpErrorResponse) => {
          reject(e);
        }
      }
      );
    });
  }
  getAPITokenObservable(): Observable<void> {
    return from(this.getAPIToken());
  }
  isAuthenticated(): boolean {
    return !!this.apiToken &&
      !!this.webCredentials &&
      !!this.user &&
      this.apiTokenExpirationTime!.isAfter(moment());
  }
  logoutHandler() {
    this.logout();
    this.bc.postMessage({
      type: BroadcastType.LOGOUT
    });
  }
  logout() {
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    google.accounts.id.disableAutoSelect();
    this.apiToken = undefined;
    this.webCredentials = undefined;
    this.user = undefined;
    this.apiTokenExpirationTime = undefined;
    window.location.reload();
  }
  // broadcastChannel
  createBroadcastChannel() {
    this.bc = new BroadcastChannel('auth');
    this.bc.onmessage = (event: MessageEvent<AuthMessage>) => {
      this.broadcastListener(event);
    }
  }
  broadcastListener(event: MessageEvent<AuthMessage>) {
    console.log('BroadcastChannel message recieved', event.data.type);
    switch (event.data.type) {
      case BroadcastType.REQUEST_AUTH_DATA:
        this.sendAuthData();
        break;
      case BroadcastType.SEND_AUTH_DATA:
        this.recieveAuthData(event.data.data!);
        break;
      case BroadcastType.LOGOUT:
        this.logout();
        break;
      default:
        break;
    }
  }
  askForAuthData() {
    this.bc.postMessage({
      type: BroadcastType.REQUEST_AUTH_DATA
    });
  }
  sendAuthData() {
    if (this.isAuthenticated()) {
      this.bc.postMessage({
        type: BroadcastType.SEND_AUTH_DATA,
        data: {
          webCredentials: this.webCredentials,
          apiToken: this.apiToken,
          apiTokenExpirationTime: this.apiTokenExpirationTime?.toISOString()
        }
      });
    }
  }
  recieveAuthData(data: AuthData) {
    this.webCredentials = data.webCredentials;
    this.apiToken = data.apiToken;
    this.apiTokenExpirationTime = moment(data.apiTokenExpirationTime);
    this.retrieveUserFromCredentials(this.webCredentials);
  }
}

enum BroadcastType {
  REQUEST_AUTH_DATA = 'request',
  SEND_AUTH_DATA = 'send',
  LOGOUT = 'logout'
}
interface AuthData {
  webCredentials: string,
  apiToken: string,
  apiTokenExpirationTime: string
}
interface AuthMessage {
  sender: string,
  type: BroadcastType,
  data?: AuthData
}

