/* eslint-disable @typescript-eslint/member-ordering */
import {
  Component,
  OnDestroy,
  OnInit,
  computed,
  inject,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
import type {
  DesktopSettingsPatch,
  DesktopSettingsSnapshot,
  LocalApiSettings,
  LocalApiSnapshot
} from '../../../../core/platform/electron/electron-api.models';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';

@Component({
  selector: 'app-local-api-settings',
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    ...APP_TRANSLATE_IMPORTS
  ],
  templateUrl: './local-api-settings.component.html'
})
export class LocalApiSettingsComponent implements OnInit, OnDestroy {
  private readonly bridge = inject(ElectronBridgeService);
  private readonly appI18n = inject(AppI18nService);

  readonly isElectron = this.bridge.isAvailable;

  readonly settings = signal<LocalApiSettings>({
    enabled: false,
    port: 17878,
    exposeOnLan: false,
    scalarEnabled: false,
    docusaurusEnabled: false,
    allowedSignalingServers: []
  });

  readonly status = signal<LocalApiSnapshot>({
    status: 'stopped',
    host: null,
    port: null,
    baseUrl: null,
    error: null,
    exposeOnLan: false,
    scalarEnabled: false,
    docusaurusEnabled: false
  });

  readonly allowedServersText = signal('');
  readonly hasPendingAllowedServersChanges = signal(false);
  readonly portText = signal('17878');
  readonly hasPendingPortChange = signal(false);
  readonly busy = signal(false);
  readonly errorMessage = signal<string | null>(null);

  readonly statusLabel = computed(() => {
    const snapshot = this.status();

    switch (snapshot.status) {
      case 'running':
        return this.appI18n.instant('settings.localApi.status.running', {
          url: snapshot.baseUrl ?? this.appI18n.instant('settings.localApi.status.unknown')
        });
      case 'starting':
        return this.appI18n.instant('settings.localApi.status.starting');
      case 'error':
        return this.appI18n.instant('settings.localApi.status.error', {
          message: snapshot.error ?? this.appI18n.instant('settings.localApi.status.unknown')
        });
      case 'stopped':
      default:
        return this.appI18n.instant('settings.localApi.status.stopped');
    }
  });

  private statusPollHandle: ReturnType<typeof setInterval> | null = null;

  async ngOnInit(): Promise<void> {
    if (!this.isElectron)
      return;

    await this.refresh();

    this.statusPollHandle = setInterval(() => {
      void this.refreshStatus();
    }, 3000);
  }

  ngOnDestroy(): void {
    if (this.statusPollHandle) {
      clearInterval(this.statusPollHandle);
      this.statusPollHandle = null;
    }
  }

  async refresh(): Promise<void> {
    const api = this.bridge.getApi();

    if (!api)
      return;

    const desktop: DesktopSettingsSnapshot = await api.getDesktopSettings();

    this.settings.set({
      enabled: desktop.localApi.enabled,
      port: desktop.localApi.port,
      exposeOnLan: desktop.localApi.exposeOnLan,
      scalarEnabled: desktop.localApi.scalarEnabled,
      docusaurusEnabled: desktop.localApi.docusaurusEnabled,
      allowedSignalingServers: [...desktop.localApi.allowedSignalingServers]
    });

    this.allowedServersText.set(desktop.localApi.allowedSignalingServers.join('\n'));
    this.hasPendingAllowedServersChanges.set(false);
    this.portText.set(String(desktop.localApi.port));
    this.hasPendingPortChange.set(false);

    await this.refreshStatus();
  }

  async refreshStatus(): Promise<void> {
    const api = this.bridge.getApi();

    if (!api)
      return;

    const snapshot = await api.getLocalApiStatus();

    if (snapshot)
      this.status.set(snapshot);
  }

  async toggleEnabled(event: Event): Promise<void> {
    const checkbox = event.target as HTMLInputElement;

    await this.savePatch({ enabled: checkbox.checked });
  }

  async toggleExposeOnLan(event: Event): Promise<void> {
    const checkbox = event.target as HTMLInputElement;

    await this.savePatch({ exposeOnLan: checkbox.checked });
  }

  async toggleScalar(event: Event): Promise<void> {
    const checkbox = event.target as HTMLInputElement;

    await this.savePatch({ scalarEnabled: checkbox.checked });
  }

  async toggleDocusaurus(event: Event): Promise<void> {
    const checkbox = event.target as HTMLInputElement;

    await this.savePatch({ docusaurusEnabled: checkbox.checked });
  }

  onPortInput(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.portText.set(input.value);
    this.hasPendingPortChange.set(true);
  }

  async savePort(): Promise<void> {
    const parsed = Number(this.portText());

    if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) {
      this.errorMessage.set(this.appI18n.instant('settings.localApi.errors.invalidPort'));
      return;
    }

    this.errorMessage.set(null);
    await this.savePatch({ port: Math.floor(parsed) });
    this.hasPendingPortChange.set(false);
  }

  onAllowedServersInput(event: Event): void {
    const textarea = event.target as HTMLTextAreaElement;

    this.allowedServersText.set(textarea.value);
    this.hasPendingAllowedServersChanges.set(true);
  }

  async saveAllowedServers(): Promise<void> {
    const list = this.allowedServersText()
      .split(/\r?\n/u)
      .map((line) => line.trim())
      .filter((line) => line.length > 0);

    await this.savePatch({ allowedSignalingServers: list });
    this.hasPendingAllowedServersChanges.set(false);
  }

  async openDocs(): Promise<void> {
    const api = this.bridge.getApi();

    if (!api)
      return;

    const result = await api.openLocalApiDocs();

    if (result && !result.opened) {
      this.errorMessage.set(result.reason ?? this.appI18n.instant('settings.localApi.errors.couldNotOpenDocs'));
    } else {
      this.errorMessage.set(null);
    }
  }

  async openDocusaurusDocs(): Promise<void> {
    const api = this.bridge.getApi();

    if (!api)
      return;

    const result = await api.openDocusaurusDocs();

    if (result && !result.opened) {
      this.errorMessage.set(result.reason ?? this.appI18n.instant('settings.localApi.errors.couldNotOpenDocs'));
    } else {
      this.errorMessage.set(null);
      await this.refresh();
    }
  }

  async copyBaseUrl(): Promise<void> {
    const baseUrl = this.status().baseUrl;

    if (!baseUrl)
      return;

    try {
      await navigator.clipboard.writeText(baseUrl);
    } catch {
      // ignore clipboard errors
    }
  }

  private async savePatch(localApiPatch: Partial<LocalApiSettings>): Promise<void> {
    if (this.busy())
      return;

    const api = this.bridge.getApi();

    if (!api)
      return;

    this.busy.set(true);
    this.errorMessage.set(null);

    try {
      const patch: DesktopSettingsPatch = { localApi: localApiPatch };
      const updated = await api.setDesktopSettings(patch);

      this.settings.set({
        enabled: updated.localApi.enabled,
        port: updated.localApi.port,
        exposeOnLan: updated.localApi.exposeOnLan,
        scalarEnabled: updated.localApi.scalarEnabled,
        docusaurusEnabled: updated.localApi.docusaurusEnabled,
        allowedSignalingServers: [...updated.localApi.allowedSignalingServers]
      });

      this.portText.set(String(updated.localApi.port));
      this.allowedServersText.set(updated.localApi.allowedSignalingServers.join('\n'));

      await this.refreshStatus();
    } catch (error) {
      this.errorMessage.set((error as Error).message ?? this.appI18n.instant('settings.localApi.errors.updateFailed'));
    } finally {
      this.busy.set(false);
    }
  }
}
