import {
  Component,
  HostListener,
  computed,
  effect,
  inject,
  input,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideBug } from '@ng-icons/lucide';

import { DebuggingService, type DebugLogLevel } from '../../../core/services/debugging.service';
import { DebugConsoleEntryListComponent } from './debug-console-entry-list/debug-console-entry-list.component';
import { DebugConsoleNetworkMapComponent } from './debug-console-network-map/debug-console-network-map.component';
import { DebugConsoleToolbarComponent } from './debug-console-toolbar/debug-console-toolbar.component';
import { DebugConsoleResizeService } from './services/debug-console-resize.service';
import { DebugConsoleExportService, type DebugExportFormat } from './services/debug-console-export.service';
import { DebugConsoleEnvironmentService } from './services/debug-console-environment.service';
import { APP_TRANSLATE_IMPORTS } from '../../../core/i18n';

type DebugLevelState = Record<DebugLogLevel, boolean>;

type DebugConsoleTab = 'logs' | 'network';
type DebugConsoleLauncherVariant = 'floating' | 'inline' | 'compact';

@Component({
  selector: 'app-debug-console',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    DebugConsoleEntryListComponent,
    DebugConsoleNetworkMapComponent,
    DebugConsoleToolbarComponent,
    ...APP_TRANSLATE_IMPORTS
  ],
  viewProviders: [
    provideIcons({
      lucideBug
    })
  ],
  templateUrl: './debug-console.component.html',
  host: {
    style: 'display: contents;',
    'data-debug-console-root': 'true'
  }
})
export class DebugConsoleComponent {
  private static readonly VISIBLE_ENTRY_LIMIT = 500;

  readonly debugging = inject(DebuggingService);
  readonly resizeService = inject(DebugConsoleResizeService);
  readonly exportService = inject(DebugConsoleExportService);
  readonly envService = inject(DebugConsoleEnvironmentService);
  readonly entries = this.debugging.entries;
  readonly isOpen = this.debugging.isConsoleOpen;
  readonly networkSnapshot = this.debugging.networkSnapshot;
  readonly launcherVariant = input<DebugConsoleLauncherVariant>('floating');
  readonly showLauncher = input(true);
  readonly showPanel = input(true);

  readonly activeTab = signal<DebugConsoleTab>('logs');
  readonly detached = signal(false);
  readonly searchTerm = signal('');
  readonly selectedSource = signal('all');
  readonly autoScroll = signal(true);
  readonly showAllEntries = signal(false);
  readonly panelHeight = this.resizeService.panelHeight;
  readonly panelWidth = this.resizeService.panelWidth;
  readonly panelLeft = this.resizeService.panelLeft;
  readonly panelTop = this.resizeService.panelTop;
  readonly levelState = signal<DebugLevelState>({
    event: true,
    info: true,
    warn: true,
    error: true,
    debug: true
  });

  readonly sourceOptions = computed(() => {
    return Array.from(new Set(this.entries().map((entry) => entry.source))).sort();
  });

  readonly filteredEntries = computed(() => {
    const searchTerm = this.searchTerm().trim()
      .toLowerCase();
    const selectedSource = this.selectedSource();
    const levelState = this.levelState();

    return this.entries().filter((entry) => {
      if (!levelState[entry.level])
        return false;

      if (selectedSource !== 'all' && entry.source !== selectedSource)
        return false;

      if (!searchTerm)
        return true;

      return [
        entry.message,
        entry.source,
        entry.level,
        entry.timeLabel,
        entry.dateTimeLabel,
        entry.payloadText || ''
      ].some((value) => value.toLowerCase().includes(searchTerm));
    });
  });
  readonly visibleEntries = computed(() => {
    const all = this.filteredEntries();

    if (this.showAllEntries() || all.length <= DebugConsoleComponent.VISIBLE_ENTRY_LIMIT)
      return all;

    return all.slice(-DebugConsoleComponent.VISIBLE_ENTRY_LIMIT);
  });
  readonly isTruncated = computed(() => {
    return !this.showAllEntries() && this.filteredEntries().length > DebugConsoleComponent.VISIBLE_ENTRY_LIMIT;
  });
  readonly levelCounts = computed<Record<DebugLogLevel, number>>(() => {
    const counts: Record<DebugLogLevel, number> = {
      event: 0,
      info: 0,
      warn: 0,
      error: 0,
      debug: 0
    };

    for (const entry of this.entries()) {
      counts[entry.level] += entry.count;
    }

    return counts;
  });
  readonly visibleCount = computed(() => {
    return this.visibleEntries().reduce((sum, entry) => sum + entry.count, 0);
  });
  readonly badgeCount = computed(() => {
    const counts = this.levelCounts();

    return counts.error > 0 ? counts.error : this.entries().reduce((sum, entry) => sum + entry.count, 0);
  });
  readonly hasErrors = computed(() => this.levelCounts().error > 0);
  readonly networkSummary = computed(() => this.networkSnapshot().summary);

  constructor() {
    this.resizeService.syncBounds(this.detached());

    effect(() => {
      const selectedSource = this.selectedSource();
      const sourceOptions = this.sourceOptions();

      if (selectedSource !== 'all' && !sourceOptions.includes(selectedSource))
        this.selectedSource.set('all');
    });
  }

  @HostListener('window:mousemove', ['$event'])
  onResizeMove(event: MouseEvent): void {
    this.resizeService.onMouseMove(event, this.detached());
  }

  @HostListener('window:mouseup')
  onResizeEnd(): void {
    this.resizeService.onMouseUp();
  }

  @HostListener('window:resize')
  onWindowResize(): void {
    this.resizeService.syncBounds(this.detached());
  }

  toggleConsole(): void {
    this.debugging.toggleConsole();
  }

  closeConsole(): void {
    this.debugging.closeConsole();
  }

  updateSearchTerm(value: string): void {
    this.searchTerm.set(value);
  }

  updateSelectedSource(source: string): void {
    this.selectedSource.set(source);
  }

  setActiveTab(tab: DebugConsoleTab): void {
    this.activeTab.set(tab);
  }

  exportLogs(format: DebugExportFormat): void {
    const env = this.envService.getEnvironment();
    const name = this.envService.getFilenameSafeDisplayName();

    this.exportService.exportLogs(
      this.filteredEntries(),
      format,
      env,
      name
    );
  }

  exportNetwork(format: DebugExportFormat): void {
    const env = this.envService.getEnvironment();
    const name = this.envService.getFilenameSafeDisplayName();

    this.exportService.exportNetwork(
      this.networkSnapshot(),
      format,
      env,
      name
    );
  }

  toggleDetached(): void {
    const nextDetached = !this.detached();

    this.detached.set(nextDetached);
    this.resizeService.syncBounds(nextDetached);

    if (nextDetached)
      this.resizeService.initializeDetachedPosition();
  }

  toggleLevel(level: DebugLogLevel): void {
    this.levelState.update((current) => ({
      ...current,
      [level]: !current[level]
    }));
  }

  toggleAutoScroll(): void {
    this.autoScroll.update((enabled) => !enabled);
  }

  disableAutoScroll(): void {
    this.autoScroll.set(false);
  }

  clearLogs(): void {
    this.debugging.clear();
    this.showAllEntries.set(false);
  }

  toggleShowAllEntries(): void {
    this.showAllEntries.update((isVisible) => !isVisible);
  }

  startTopResize(event: MouseEvent): void {
    this.resizeService.startTopResize(event);
  }

  startBottomResize(event: MouseEvent): void {
    this.resizeService.startBottomResize(event);
  }

  startLeftResize(event: MouseEvent): void {
    this.resizeService.startLeftResize(event);
  }

  startRightResize(event: MouseEvent): void {
    this.resizeService.startRightResize(event);
  }

  startCornerResize(event: MouseEvent): void {
    this.resizeService.startCornerResize(event);
  }

  startDrag(event: MouseEvent): void {
    if (!this.detached())
      return;

    this.resizeService.startDrag(event);
  }

  formatBadgeCount(count: number): string {
    if (count > 99)
      return '99+';

    return count.toString();
  }
}
