import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, catchError, map, of, shareReplay, switchMap, take, tap } from 'rxjs';
import { WidgetManifestEntry, WidgetManifestFile } from '../contracts/widget-manifest.contract';
import { ConfigService } from '../../core/config/config.service';

@Injectable({ providedIn: 'root' })
export class WidgetManifestService {
  private readonly fallbackManifestUrl = '/assets/mock/bootstrap/widget-manifest.json';
  private readonly manifestByUrl = new Map<string, Observable<WidgetManifestFile>>();
  /** Last manifest resolved by getManifest(), for synchronous readers (e.g. ProjectValidator) that can't await an Observable. Mirrors ConfigService.getBootstrapSnapshot(). */
  private manifestSnapshot: WidgetManifestFile | null = null;

  constructor(
    private readonly http: HttpClient,
    private readonly configService: ConfigService
  ) {}

  getManifest(): Observable<WidgetManifestFile> {
    return this.resolveManifestUrl().pipe(
      switchMap((manifestUrl) => {
        const cached = this.manifestByUrl.get(manifestUrl);
        if (cached) {
          return cached;
        }

        const manifest$ = this.http.get<WidgetManifestFile>(manifestUrl).pipe(
          tap(manifest => { this.manifestSnapshot = manifest; }),
          shareReplay({ bufferSize: 1, refCount: true }),
          catchError(() => of({ widgets: [] }))
        );

        this.manifestByUrl.set(manifestUrl, manifest$);
        return manifest$;
      })
    );
  }

  getWidgets(): Observable<WidgetManifestEntry[]> {
    return this.getManifest().pipe(map((manifest) => manifest.widgets ?? []));
  }

  getWidget(type: string): Observable<WidgetManifestEntry | undefined> {
    return this.getWidgets().pipe(map((widgets) => widgets.find((widget) => widget.type === type)));
  }

  /** Synchronous accessor for the last-resolved manifest, or null before it's loaded once. */
  getManifestSnapshot(): WidgetManifestFile | null {
    return this.manifestSnapshot;
  }

  private resolveManifestUrl(): Observable<string> {
    const snapshotUrl = this.configService.getBootstrapSnapshot()?.widgetRegistry?.manifestUrl;
    if (snapshotUrl) {
      return of(snapshotUrl);
    }

    return this.configService.loadBootstrap().pipe(
      take(1),
      map((bootstrap) => bootstrap.widgetRegistry?.manifestUrl || this.fallbackManifestUrl),
      catchError(() => of(this.fallbackManifestUrl))
    );
  }
}