import { DOCUMENT } from '@angular/common';
import {
  Injectable,
  inject,
  signal
} from '@angular/core';

import { SettingsModalService, type SettingsPage } from '../../../../core/services/settings-modal.service';
import { ThemeRegistryService } from './theme-registry.service';

@Injectable({ providedIn: 'root' })
export class ElementPickerService {
  readonly isPicking = signal(false);
  readonly hoveredKey = signal<string | null>(null);
  readonly selectedKey = signal<string | null>(null);

  readonly documentRef = inject(DOCUMENT);
  readonly modal = inject(SettingsModalService);
  readonly registry = inject(ThemeRegistryService);

  private removeListeners: (() => void)[] = [];
  private resumePage: SettingsPage | null = null;
  private shouldRestoreModalOnCancel = true;

  start(page: SettingsPage = 'theme', restoreModalOnCancel = true): void {
    if (this.isPicking()) {
      return;
    }

    this.resumePage = page;
    this.shouldRestoreModalOnCancel = restoreModalOnCancel;
    this.modal.close();
    this.attachListeners();
    this.hoveredKey.set(null);
    this.isPicking.set(true);
  }

  cancel(): void {
    if (!this.isPicking()) {
      return;
    }

    this.detachListeners();
    this.hoveredKey.set(null);
    this.isPicking.set(false);

    if (this.shouldRestoreModalOnCancel && this.resumePage) {
      this.modal.open(this.resumePage);
    }
  }

  clearSelection(): void {
    this.selectedKey.set(null);
  }

  private completePick(key: string): void {
    this.selectedKey.set(key);
    this.detachListeners();
    this.hoveredKey.set(null);
    this.isPicking.set(false);

    if (this.resumePage === 'theme') {
      this.modal.openThemeStudio();
      return;
    }

    if (this.resumePage) {
      this.modal.open(this.resumePage);
    }
  }

  private attachListeners(): void {
    const onPointerMove = (event: Event) => {
      const key = this.resolveThemeKeyFromTarget(event.target);

      this.hoveredKey.set(key);
    };
    const onClick = (event: Event) => {
      const key = this.resolveThemeKeyFromTarget(event.target);

      if (!key) {
        return;
      }

      event.preventDefault();
      event.stopPropagation();

      if ('stopImmediatePropagation' in event) {
        (event as Event & { stopImmediatePropagation(): void }).stopImmediatePropagation();
      }

      this.completePick(key);
    };
    const onKeyDown = (event: Event) => {
      const keyboardEvent = event as KeyboardEvent;

      if (keyboardEvent.key === 'Escape') {
        keyboardEvent.preventDefault();
        keyboardEvent.stopPropagation();
        this.cancel();
      }
    };

    this.documentRef.addEventListener('pointermove', onPointerMove, true);
    this.documentRef.addEventListener('click', onClick, true);
    this.documentRef.addEventListener('keydown', onKeyDown, true);

    this.removeListeners = [
      () => this.documentRef.removeEventListener('pointermove', onPointerMove, true),
      () => this.documentRef.removeEventListener('click', onClick, true),
      () => this.documentRef.removeEventListener('keydown', onKeyDown, true)
    ];
  }

  private detachListeners(): void {
    for (const removeListener of this.removeListeners) {
      removeListener();
    }

    this.removeListeners = [];
  }

  private resolveThemeKeyFromTarget(target: EventTarget | null): string | null {
    if (!(target instanceof Element)) {
      return null;
    }

    const themedElement = target.closest<HTMLElement>('[data-theme-key]');
    const key = themedElement?.dataset['themeKey'] ?? null;
    const definition = this.registry.getDefinition(key);

    return definition?.pickerVisible ? key : null;
  }
}
