import { animate, style, transition, trigger } from '@angular/animations';
import { NgClass } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject, input, model, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TuiCheckbox } from '@taiga-ui/core/components/checkbox';
import { TuiButton } from '@taiga-ui/core/components/button';
import { TuiLoader } from '@taiga-ui/core/components/loader';
import { TuiChip } from '@taiga-ui/kit/components/chip';

import { isMouseMoveTelemetryEvent } from '../../../../core/mouse/mouse-payload.util';
import { SessionStatusChipClassesPipe } from '../../../../core/sessions/session-status-chip-classes.pipe';
import { SessionStatusPipe } from '../../../../core/sessions/session-status.pipe';
import { TelemetryEventTypePipe } from '../../../../core/sessions/telemetry-event-type.pipe';
import { summarizeTelemetryData } from '../../../../core/sessions/telemetry-event-summary.engine';
import { SessionsApiService } from '../../../../core/services/sessions-api.service';
import type { ParsedEvent, SessionDetailResponse } from '../../../../core/models/api.types';
import { formatTimestamp, formatUnixMs as formatUnixMsUtil } from '../../../../shared/utils/date-time.util';
import { clamp } from '../../../../shared/utils/math.util';
import { resolveActiveStreamType, resolvePlaylistUrlForType } from '../../../../shared/utils/stream.util';
import { HlsPlayerComponent } from '../../hls-player/hls-player.component';
import { StreamSelectorComponent } from '../../stream-selector/stream-selector.component';
import { TelemetryEventDetailComponent } from '../../telemetry-event-detail/telemetry-event-detail.component';
import type { TelemetryLoadState, TelemetryRangeSelection } from '../session-detail.types';

@Component({
  selector: 'app-session-view-tab',
  imports: [
    NgClass,
    FormsModule,
    TuiButton,
    TuiCheckbox,
    TuiChip,
    TuiLoader,
    SessionStatusChipClassesPipe,
    SessionStatusPipe,
    TelemetryEventTypePipe,
    HlsPlayerComponent,
    StreamSelectorComponent,
    TelemetryEventDetailComponent,
  ],
  templateUrl: './session-view-tab.html',
  styleUrl: './session-view-tab.css',
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [
    trigger('telemetryEventDetail', [
      transition(':enter', [
        style({ opacity: 0, transform: 'translateY(-0.4rem)' }),
        animate(
          '220ms cubic-bezier(0.33, 1, 0.68, 1)',
          style({ opacity: 1, transform: 'translateY(0)' }),
        ),
      ]),
      transition(':leave', [
        animate(
          '170ms cubic-bezier(0.4, 0, 1, 1)',
          style({ opacity: 0, transform: 'translateY(-0.3rem)' }),
        ),
      ]),
    ]),
  ],
})
export class SessionViewTabComponent {
  private readonly api = inject(SessionsApiService);

  readonly detail = input.required<SessionDetailResponse>();
  readonly telemetryState = input.required<TelemetryLoadState>();
  readonly recordingStartMs = input.required<number | null>();
  readonly recordingEndMs = input.required<number | null>();
  readonly excludeMouseMoves = model(true);
  readonly telemetryToMsChange = output<number>();

  protected readonly selectedStreamType = signal<string | null>(null);
  protected readonly telemetryEventTypeFilter = signal<string | null>(null);
  protected readonly expandedTelemetryRowKey = signal<string | null>(null);
  protected readonly customToLocal = signal('');
  private readonly telemetryRangeSelection = signal<TelemetryRangeSelection>({ type: 'end' });

  protected activeStreamType(): string | null {
    return resolveActiveStreamType(this.detail().streams, this.selectedStreamType());
  }

  protected playlistUrl(): string | null {
    return resolvePlaylistUrlForType(this.detail().streams, this.activeStreamType(), this.api);
  }

  protected pickStream(type: string): void {
    this.selectedStreamType.set(type);
  }

  protected pickTelemetryEventTypeFilter(type: string | null): void {
    this.telemetryEventTypeFilter.set(type);
    this.expandedTelemetryRowKey.set(null);
  }

  protected telemetryEventTypeKey(event: ParsedEvent): string {
    const t = event.event_type;
    if (t == null || t === '') {
      return '';
    }
    return String(t).trim().toLowerCase();
  }

  protected uniqueTelemetryEventTypes(events: ParsedEvent[]): string[] {
    const set = new Set<string>();
    for (const e of events) {
      set.add(this.telemetryEventTypeKey(e));
    }
    return [...set].sort((a, b) => a.localeCompare(b, 'ru'));
  }

  protected telemetryEventsOfType(events: ParsedEvent[], typeKey: string): number {
    return events.filter((e) => this.telemetryEventTypeKey(e) === typeKey).length;
  }

  protected visibleTelemetryEvents(): ParsedEvent[] {
    const events = this.telemetryState().telemetry;
    if (!this.excludeMouseMoves()) {
      return events;
    }
    return events.filter((e) => !isMouseMoveTelemetryEvent(e));
  }

  protected filteredTelemetryEvents(events: ParsedEvent[]): ParsedEvent[] {
    const filter = this.telemetryEventTypeFilter();
    if (filter === null) {
      return events;
    }
    return events.filter((e) => this.telemetryEventTypeKey(e) === filter);
  }

  protected telemetryEventSummary(event: ParsedEvent): string {
    return summarizeTelemetryData(event.data);
  }

  protected telemetryRowKey(row: ParsedEvent, index: number): string {
    return `${row.timestamp}\u0000${this.telemetryEventTypeKey(row)}\u0000${index}`;
  }

  protected toggleTelemetryRow(row: ParsedEvent, index: number): void {
    const key = this.telemetryRowKey(row, index);
    this.expandedTelemetryRowKey.update((cur) => (cur === key ? null : key));
  }

  protected isTelemetryRowExpanded(row: ParsedEvent, index: number): boolean {
    return this.expandedTelemetryRowKey() === this.telemetryRowKey(row, index);
  }

  protected selectRecentWindow(seconds: number): void {
    this.customToLocal.set('');
    this.telemetryRangeSelection.set({ type: 'preset', seconds });
    const start = this.recordingStartMs() ?? Date.now();
    const end = this.recordingEndMs() ?? Date.now();
    this.telemetryToMsChange.emit(clamp(start + seconds * 1000, start, end));
  }

  protected loadUntilEndTelemetry(): void {
    this.customToLocal.set('');
    this.telemetryRangeSelection.set({ type: 'end' });
    this.telemetryToMsChange.emit(this.recordingEndMs() ?? Date.now());
  }

  protected applyCustomTo(value: string): void {
    this.customToLocal.set(value);
    if (!value) {
      return;
    }
    const ms = new Date(value).getTime();
    if (Number.isFinite(ms)) {
      this.telemetryRangeSelection.set({ type: 'custom' });
      const start = this.recordingStartMs() ?? Date.now();
      const end = this.recordingEndMs() ?? Date.now();
      this.telemetryToMsChange.emit(clamp(ms, start, end));
    }
  }

  protected telemetryRangePresetIs(seconds: number): boolean {
    const s = this.telemetryRangeSelection();
    return s.type === 'preset' && s.seconds === seconds;
  }

  protected telemetryRangeIsEnd(): boolean {
    return this.telemetryRangeSelection().type === 'end';
  }

  protected telemetryRangeLabel(toMs: number): string {
    return `С ${formatUnixMsUtil(this.recordingStartMs())} до ${formatUnixMsUtil(toMs)}`;
  }

  protected formatDate(value: string | null | undefined): string {
    return formatTimestamp(value);
  }

  protected formatUnixMs(value: number | null | undefined): string {
    return formatUnixMsUtil(value);
  }
}
