import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  OnInit,
  QueryList,
  ViewChildren,
} from '@angular/core';
import { AsyncPipe, DatePipe, DecimalPipe, NgClass, SlicePipe } from '@angular/common';
import { Select, Store } from '@ngxs/store';
import { combineLatest, Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip';
import { VisionState } from '../store/vision.state';
import {
  ClearAllHistory,
  DeleteHistoryRecord,
  LoadGroupImages,
  LoadHistory,
  ToggleBatchGroup,
} from '../store/vision.actions';

interface BatchGroup {
  batchId: string;
  timestamp: string;
  totalCount: number;
  avgConfidencePct: number;
  mode: string;
  items: any[];
  isExpanded: boolean;
}

@Component({
  selector: 'app-history',
  standalone: true,
  imports: [
    AsyncPipe,
    DatePipe,
    DecimalPipe,
    NgClass,
    SlicePipe,
    MatButtonModule,
    MatIconModule,
    MatProgressSpinnerModule,
    MatTooltipModule,
  ],
  templateUrl: './history.component.html',
  styleUrl: './history.component.scss',
})
export class HistoryComponent implements OnInit, AfterViewInit, OnDestroy {
  @Select(VisionState.items) items$!: Observable<any[]>;
  @Select(VisionState.expandedBatchIds) expandedBatchIds$!: Observable<string[]>;
  @Select(VisionState.loading) loading$!: Observable<boolean>;

  @ViewChildren('thumbCanvas') thumbCanvasRefs!: QueryList<ElementRef<HTMLCanvasElement>>;

  groups$!: Observable<BatchGroup[]>;
  private currentGroups: BatchGroup[] = [];
  private groupsSub!: Subscription;
  private canvasSub!: Subscription;

  constructor(private store: Store) {}

  ngOnInit(): void {
    this.groups$ = combineLatest([this.items$, this.expandedBatchIds$]).pipe(
      map(([items, expandedIds]) => this.buildGroups(items, expandedIds)),
    );
    this.groupsSub = this.groups$.subscribe(g => (this.currentGroups = g));
    this.store.dispatch(new LoadHistory());
  }

  ngAfterViewInit(): void {
    this.canvasSub = this.thumbCanvasRefs.changes.subscribe(() =>
      this.paintAllVisibleCanvases(),
    );
  }

  ngOnDestroy(): void {
    this.groupsSub?.unsubscribe();
    this.canvasSub?.unsubscribe();
  }

  onToggle(batchId: string): void {
    this.store.dispatch(new ToggleBatchGroup({ batchId }));
    this.store.dispatch(new LoadGroupImages({ batchId }));
  }

  onDeleteBatch(group: BatchGroup, event: MouseEvent): void {
    event.stopPropagation();
    for (const item of group.items) {
      this.store.dispatch(new DeleteHistoryRecord({ id: item.archive_id }));
    }
  }

  onDeleteItem(archiveId: string, event: MouseEvent): void {
    event.stopPropagation();
    this.store.dispatch(new DeleteHistoryRecord({ id: archiveId }));
  }

  onClearAll(): void {
    this.store.dispatch(new ClearAllHistory());
  }

  renderThumbnailWithBoxes(canvas: HTMLCanvasElement, item: any): void {
    if (!canvas || !item?.imageDataUrl) return;
    const img = new Image();
    img.onload = () => {
      canvas.width = canvas.offsetWidth || 148;
      canvas.height = 120;
      const ctx = canvas.getContext('2d')!;
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

      for (const det of (item.detections ?? [])) {
        let x: number, y: number, w: number, h: number;
        if (det.norm_box) {
          const [nx1, ny1, nx2, ny2] = det.norm_box;
          x = nx1 * canvas.width;
          y = ny1 * canvas.height;
          w = (nx2 - nx1) * canvas.width;
          h = (ny2 - ny1) * canvas.height;
        } else if (det.box) {
          const scaleX = canvas.width / img.naturalWidth;
          const scaleY = canvas.height / img.naturalHeight;
          const [x1, y1, x2, y2] = det.box;
          x = x1 * scaleX;
          y = y1 * scaleY;
          w = (x2 - x1) * scaleX;
          h = (y2 - y1) * scaleY;
        } else {
          continue;
        }
        const color = this.gradeColor(det.class);

        ctx.strokeStyle = color;
        ctx.lineWidth = 1.5;
        ctx.strokeRect(x, y, w, h);

        ctx.font = 'bold 9px sans-serif';
        const label = det.class;
        const tw = ctx.measureText(label).width;
        ctx.fillStyle = color;
        ctx.fillRect(x, y - 14, tw + 4, 14);
        ctx.fillStyle = '#fff';
        ctx.fillText(label, x + 2, y - 3);
      }
    };
    img.src = item.imageDataUrl;
  }

  confidencePercent(value: number): string {
    return (value * 100).toFixed(1) + '%';
  }

  gradeColor(cls: string): string {
    const palette: Record<string, string> = {
      Ripe: '#4caf50',
      Unripe: '#ff9800',
      Underripe: '#ffeb3b',
      Overripe: '#9c27b0',
      Abnormal: '#f44336',
      Empty_Bunch: '#607d8b',
    };
    return palette[cls] ?? '#757575';
  }

  private buildGroups(items: any[], expandedIds: string[]): BatchGroup[] {
    if (!items || !Array.isArray(items)) {
      return [];
    }

    const groupMap = new Map<string, any[]>();
    for (const item of (items ?? [])) {
      const bid = item.batch_id ?? 'unknown';
      if (!groupMap.has(bid)) groupMap.set(bid, []);
      groupMap.get(bid)!.push(item);
    }

    return Array.from(groupMap.entries()).map(([batchId, batchItems]) => {
      const totalCount = batchItems.reduce((s, i) => s + (i.total_count ?? 0), 0);
      const allDetections: any[] = batchItems.flatMap((i: any) => i.detections ?? []);
      const avgConfidencePct = allDetections.length
        ? (allDetections.reduce((s, d) => s + (d.confidence ?? 0), 0) / allDetections.length) * 100
        : 0;

      return {
        batchId,
        timestamp: batchItems[0]?.created_at ?? batchItems[0]?.timestamp ?? '',
        totalCount,
        avgConfidencePct,
        mode: batchItems[0]?.mode ?? 'remote',
        items: batchItems,
        isExpanded: expandedIds.includes(batchId),
      };
    });
  }

  private paintAllVisibleCanvases(): void {
    setTimeout(() => {
      this.thumbCanvasRefs.forEach(ref => {
        const canvas = ref.nativeElement;
        const archiveId = canvas.getAttribute('data-archive-id');
        const item = this.currentGroups
          .flatMap(g => g.items)
          .find(i => i.archive_id === archiveId);
        if (item?.imageDataUrl) {
          this.renderThumbnailWithBoxes(canvas, item);
        }
      });
    }, 0);
  }
}
