import { Injectable } from '@angular/core';
import { Action, Selector, State, StateContext, NgxsOnInit } from '@ngxs/store';
import { Observable, catchError, map, merge, of, switchMap, tap, timeout, toArray } from 'rxjs';
import { InferenceFrame, InferenceService } from '../services/inference.service';
import { RemoteInferenceService } from '../services/remote-inference.service';
import {
  ClearAllHistory,
  DeleteHistoryRecord,
  LoadGroupImages,
  LoadHistory,
  SubmitBatchAnalysis,
  ToggleBatchGroup,
} from './vision.actions';

export interface VisionStateModel {
  items: any[];
  loading: boolean;
  expandedBatchIds: string[];
  currentInference: any | null;
  batchFrames: any[];
  selectedFrameIndex: number | null;
}

const defaults: VisionStateModel = {
  items: [],
  loading: false,
  expandedBatchIds: [],
  currentInference: null,
  batchFrames: [],
  selectedFrameIndex: null,
};

@State<VisionStateModel>({
  name: 'visionState',
  defaults,
})
@Injectable()
export class VisionState implements NgxsOnInit {
  constructor(
    private inferenceService: InferenceService,
    private remoteInferenceService: RemoteInferenceService,
  ) {}

  /**
   * NGXS Lifecycle Hook — Executes automatically upon application state tree initialization.
   * Forces the clearing of persistent cache locks to prevent UI deadlocks from local storage.
   */
  ngxsOnInit(ctx: StateContext<VisionStateModel>): void {
    ctx.patchState({
      loading: false,
      currentInference: null,
    });
  }

  @Selector()
  static items(state: VisionStateModel): any[] {
    return state.items;
  }

  @Selector()
  static loading(state: VisionStateModel): boolean {
    return state.loading;
  }

  @Selector()
  static expandedBatchIds(state: VisionStateModel): string[] {
    return state.expandedBatchIds;
  }

  @Selector()
  static currentInference(state: VisionStateModel): any | null {
    return state.currentInference;
  }

  @Action(SubmitBatchAnalysis)
  submitBatchAnalysis(
    ctx: StateContext<VisionStateModel>,
    { payload }: SubmitBatchAnalysis,
  ): Observable<InferenceFrame[]> {
    ctx.patchState({ loading: true, currentInference: null });
    const batchId = crypto.randomUUID();

    const streams: Observable<InferenceFrame>[] = payload.files.map(file => {
      if (payload.mode === 'remote') {
        return this.remoteInferenceService.analyze(file, undefined, batchId);
      } else {
        // Pass the explicit mode string ('local-onnx' or 'local-tflite') to the local service engine
        return this.inferenceService.analyze(file, batchId, payload.mode).pipe(
          switchMap((localFrame: InferenceFrame) =>
            this.remoteInferenceService.saveExternalResult({
              frame: localFrame.imageDataUrl,
              filename: file.name,
              batchId: batchId,
              detections: localFrame.detections,
              industrial_summary: localFrame.industrial_summary,
              inference_ms: localFrame.inference_ms,
              processing_ms: localFrame.processing_ms,
            }).pipe(
              timeout(5000),
              map(res => ({ ...localFrame, frameId: res.archive_id, mode: payload.mode })),
              catchError(() => of({ ...localFrame, mode: payload.mode })),
            ),
          ),
        );
      }
    });

    return merge(...streams).pipe(
      tap(frame => ctx.patchState({ currentInference: frame })),
      toArray(),
      tap(frames => {
        ctx.patchState({
          batchFrames: frames,
          selectedFrameIndex: frames.length > 0 ? 0 : null,
          loading: false,
        });
        ctx.dispatch(new LoadHistory());
      }),
    );
  }

  @Action(ToggleBatchGroup)
  toggleBatchGroup(
    ctx: StateContext<VisionStateModel>,
    { payload }: ToggleBatchGroup,
  ): void {
    const { expandedBatchIds } = ctx.getState();
    const isExpanded = expandedBatchIds.includes(payload.batchId);
    ctx.patchState({
      expandedBatchIds: isExpanded
        ? expandedBatchIds.filter(id => id !== payload.batchId)
        : [...expandedBatchIds, payload.batchId],
    });
  }

  @Action(LoadGroupImages)
  loadGroupImages(
    ctx: StateContext<VisionStateModel>,
    { payload }: LoadGroupImages,
  ): Observable<void> {
    const { items } = ctx.getState();
    const pending = items.filter(
      (item: any) => item.batch_id === payload.batchId && !item.imageDataUrl,
    );

    if (!pending.length) return of(void 0);

    return merge(
      ...pending.map((item: any) =>
        this.remoteInferenceService.getImage(item.archive_id).pipe(
          tap(res => {
            const current = ctx.getState().items;
            ctx.patchState({
              items: current.map((i: any) => {
                // Resolve the identifier matching key from snake_case or camelCase network variants
                const r = res as any;
                const inboundId = r?.archive_id || r?.archiveId;
                return i.archive_id === inboundId ? { ...i, imageDataUrl: (r?.image_data || r?.imageDataUrl) } : i;
              }),
            });
          }),
        ),
      ),
    ).pipe(map(() => void 0));
  }

  @Action(LoadHistory)
  loadHistory(ctx: StateContext<VisionStateModel>): Observable<void> {
    ctx.patchState({ loading: true });
    return this.remoteInferenceService.getHistory().pipe(
      timeout(30000), // Extended to 30s to shield slow edge database sweeps safely
      tap(items => ctx.patchState({ items, loading: false })),
      catchError(err => {
        console.warn('⚠️ [Vault State] Edge network connection lost or timed out:', err.message || err);
        ctx.patchState({ items: [], loading: false });
        return of(void 0);
      }),
      map(() => void 0),
    );
  }

  @Action(DeleteHistoryRecord)
  deleteHistoryRecord(
    ctx: StateContext<VisionStateModel>,
    { payload }: DeleteHistoryRecord,
  ): Observable<void> {
    return this.remoteInferenceService.deleteRecord(payload.id).pipe(
      tap(() => {
        const { items } = ctx.getState();
        ctx.patchState({
          items: items.filter((i: any) => i.archive_id !== payload.id),
        });
      }),
      map(() => void 0),
    );
  }

  @Action(ClearAllHistory)
  clearAllHistory(ctx: StateContext<VisionStateModel>): Observable<void> {
    return this.remoteInferenceService.clearHistory().pipe(
      tap(() => ctx.patchState({ items: [], expandedBatchIds: [] })),
      map(() => void 0),
    );
  }
}
