/* eslint-disable @typescript-eslint/member-ordering */
import {
  Component,
  inject,
  computed,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { firstValueFrom } from 'rxjs';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideMinus,
  lucideSquare,
  lucideX,
  lucideChevronLeft,
  lucideHash,
  lucideMenu,
  lucidePackage,
  lucideRefreshCw,
  lucideShield
} from '@ng-icons/lucide';
import { NavigationEnd, Router } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { filter, map } from 'rxjs';
import {
  selectCurrentRoom,
  selectActiveChannelId,
  selectTextChannels,
  selectVoiceChannels,
  selectIsSignalServerReconnecting,
  selectSignalServerCompatibilityError
} from '../../../store/rooms/rooms.selectors';
import { MessagesActions } from '../../../store/messages/messages.actions';
import { RoomsActions } from '../../../store/rooms/rooms.actions';
import { UsersActions } from '../../../store/users/users.actions';
import { selectCurrentUser } from '../../../store/users/users.selectors';
import { ElectronBridgeService } from '../../../core/platform/electron/electron-bridge.service';
import { RealtimeSessionFacade } from '../../../core/realtime';
import { ServerDirectoryFacade } from '../../../domains/server-directory';
import { PlatformService } from '../../../core/platform';
import { clearStoredCurrentUserId } from '../../../core/storage/current-user-storage';
import { buildLoginReturnQueryParams } from '../../../domains/authentication/domain/logic/auth-navigation.rules';
import { SettingsModalService } from '../../../core/services/settings-modal.service';
import { LeaveServerDialogComponent, ModalBackdropComponent } from '../../../shared';
import { Room, type PluginRequirementSummary } from '../../../shared-kernel';
import { VoiceWorkspaceService } from '../../../domains/voice-session';
import { ThemeNodeDirective } from '../../../domains/theme';
import {
  PluginRegistryService,
  PluginRequirementStateService,
  PluginStoreService
} from '../../../domains/plugins';
import { getPluginInstallScope } from '../../../domains/plugins/domain/logic/plugin-install-scope.logic';
import { AppI18nService, APP_TRANSLATE_IMPORTS } from '../../../core/i18n';

@Component({
  selector: 'app-title-bar',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    LeaveServerDialogComponent,
    ThemeNodeDirective,
    ModalBackdropComponent,
    ...APP_TRANSLATE_IMPORTS
  ],
  viewProviders: [
    provideIcons({ lucideMinus,
      lucideSquare,
      lucideX,
      lucideChevronLeft,
      lucideHash,
      lucideMenu,
      lucidePackage,
      lucideRefreshCw,
      lucideShield })
  ],
  templateUrl: './title-bar.component.html'
})
/**
 * Electron-style title bar with window controls, navigation, and server menu.
 */
export class TitleBarComponent {
  private readonly appI18n = inject(AppI18nService);
  private store = inject(Store);
  private electronBridge = inject(ElectronBridgeService);
  private serverDirectory = inject(ServerDirectoryFacade);
  private router = inject(Router);
  private webrtc = inject(RealtimeSessionFacade);
  private platform = inject(PlatformService);
  private voiceWorkspace = inject(VoiceWorkspaceService);
  private settingsModal = inject(SettingsModalService);
  private pluginRegistry = inject(PluginRegistryService);
  private pluginRequirements = inject(PluginRequirementStateService);
  private pluginStore = inject(PluginStoreService);

  private getWindowControlsApi() {
    return this.electronBridge.getApi();
  }

  isElectron = computed(() => this.platform.isElectron);
  showMenuState = computed(() => false);

  currentUser = this.store.selectSignal(selectCurrentUser);
  username = computed(() => this.currentUser()?.displayName || this.appI18n.instant('shell.titleBar.guest'));
  serverName = computed(() => this.serverDirectory.activeServer()?.name || this.appI18n.instant('shell.titleBar.noServer'));
  isConnected = computed(() => this.webrtc.isConnected());
  isReconnecting = computed(() => !this.webrtc.isConnected() && this.webrtc.hasEverConnected());
  isAuthed = computed(() => !!this.currentUser());
  currentRoom = this.store.selectSignal(selectCurrentRoom);
  activeChannelId = this.store.selectSignal(selectActiveChannelId);
  textChannels = this.store.selectSignal(selectTextChannels);
  voiceChannels = this.store.selectSignal(selectVoiceChannels);
  isVoiceWorkspaceExpanded = this.voiceWorkspace.isExpanded;
  isSignalServerReconnecting = this.store.selectSignal(selectIsSignalServerReconnecting);
  signalServerCompatibilityError = this.store.selectSignal(selectSignalServerCompatibilityError);
  isInDirectMessage = toSignal(
    this.router.events.pipe(
      filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
      map((navigationEvent) => navigationEvent.urlAfterRedirects.startsWith('/dm/'))
    ),
    { initialValue: this.router.url.startsWith('/dm/') }
  );
  isInRoomView = toSignal(
    this.router.events.pipe(
      filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
      map((navigationEvent) => navigationEvent.urlAfterRedirects.startsWith('/room/'))
    ),
    { initialValue: this.router.url.startsWith('/room/') }
  );
  inRoom = computed(() => !!this.currentRoom() && this.isInRoomView());
  roomName = computed(() => this.currentRoom()?.name || '');
  activeTextChannelName = computed(() => {
    const textChannels = this.textChannels();

    if (textChannels.length === 0) {
      return this.appI18n.instant('shell.titleBar.noTextChannels');
    }

    const id = this.activeChannelId();
    const activeChannel = textChannels.find((channel) => channel.id === id) ?? textChannels[0];

    return activeChannel ? activeChannel.name : id;
  });
  connectedVoiceChannelName = computed(() => {
    const voiceChannelId = this.currentUser()?.voiceState?.roomId;
    const voiceChannel = this.voiceChannels().find((channel) => channel.id === voiceChannelId);

    return voiceChannel?.name || this.appI18n.instant('shell.titleBar.voiceLounge');
  });
  roomContextMeta = computed(() => {
    if (!this.currentRoom()) {
      return '';
    }

    const parts = [this.appI18n.instant('shell.titleBar.textChannelCount', { count: this.textChannels().length })];

    if (this.voiceChannels().length > 0) {
      parts.push(this.appI18n.instant('shell.titleBar.voiceChannelCount', { count: this.voiceChannels().length }));
    }

    return parts.join(' | ');
  });
  showRoomCompatibilityNotice = computed(() =>
    this.inRoom() && !!this.signalServerCompatibilityError()
  );
  showRoomReconnectNotice = computed(() =>
    this.inRoom()
    && !this.signalServerCompatibilityError()
    && (
      this.isSignalServerReconnecting()
      || this.webrtc.shouldShowConnectionError()
      || this.isReconnecting()
    )
  );
  serverPluginCount = computed(() => this.pluginRegistry.entries()
    .filter((entry) => getPluginInstallScope(entry.manifest) === 'server')
    .length);
  hasServerPlugins = computed(() => this.inRoom() && this.serverPluginCount() > 0);
  requiredPluginRequirements = this.pluginRequirements.missingRequiredRequirements;
  optionalPluginRequirement = computed(() => this.inRoom() ? this.pluginRequirements.visibleOptionalRequirements()[0] ?? null : null);
  optionalPluginRequirementCount = computed(() => this.pluginRequirements.visibleOptionalRequirements().length);
  private _showMenu = signal(false);
  showMenu = computed(() => this._showMenu());
  showLeaveConfirm = signal(false);
  inviteStatus = signal<string | null>(null);
  creatingInvite = signal(false);
  pluginRequirementBusy = signal(false);
  pluginRequirementError = signal<string | null>(null);

  /** Minimize the Electron window. */
  minimize() {
    const api = this.getWindowControlsApi();

    api?.minimizeWindow?.();
  }

  /** Maximize or restore the Electron window. */
  maximize() {
    const api = this.getWindowControlsApi();

    api?.maximizeWindow?.();
  }

  /** Close the Electron window. */
  close() {
    const api = this.getWindowControlsApi();

    api?.closeWindow?.();
  }

  /** Navigate to the login page. */
  goLogin() {
    this.router.navigate(['/login'], {
      queryParams: buildLoginReturnQueryParams(this.router.url)
    });
  }

  openPluginStore(): void {
    const returnUrl = this.router.url.startsWith('/plugin-store') ? '/dashboard' : this.router.url;

    this._showMenu.set(false);
    void this.router.navigate(['/plugin-store'], { queryParams: { returnUrl } });
  }

  openServerPlugins(): void {
    const roomId = this.currentRoom()?.id;

    if (!roomId) {
      return;
    }

    this._showMenu.set(false);
    this.settingsModal.open('serverPlugins', roomId);
  }

  openSettings(): void {
    this._showMenu.set(false);
    this.settingsModal.open('general');
  }

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

    this._showMenu.set(false);

    if (!api) {
      return;
    }

    const result = await api.openDocusaurusDocs();

    if (result && !result.opened) {
      this.inviteStatus.set(result.reason ?? this.appI18n.instant('shell.titleBar.docsOpenFailed'));
    }
  }

  /** Open the unified leave-server confirmation dialog. */
  private openLeaveConfirm() {
    this._showMenu.set(false);

    if (this.currentRoom()) {
      this.showLeaveConfirm.set(true);
    }
  }

  /** Toggle the server dropdown menu. */
  toggleMenu() {
    this.inviteStatus.set(null);
    this._showMenu.set(!this._showMenu());
  }

  /** Create a new invite link for the active room and copy it to the clipboard. */
  async createInviteLink(): Promise<void> {
    const room = this.currentRoom();
    const user = this.currentUser();

    if (!room || !user || this.creatingInvite()) {
      return;
    }

    this.creatingInvite.set(true);
    this.inviteStatus.set(this.appI18n.instant('shell.titleBar.creatingInvite'));

    try {
      const invite = await firstValueFrom(this.serverDirectory.createInvite(
        room.id,
        {
          requesterDisplayName: user.displayName,
          requesterRole: user.role
        },
        this.toSourceSelector(room)
      ));

      await this.copyInviteLink(invite.browserUrl);
      this.inviteStatus.set(this.appI18n.instant('shell.titleBar.inviteCopied'));
    } catch (error: unknown) {
      const inviteError = error as { error?: { error?: string } };

      this.inviteStatus.set(inviteError?.error?.error || this.appI18n.instant('shell.titleBar.inviteCreateFailed'));
    } finally {
      this.creatingInvite.set(false);
    }
  }

  /** Leave the current server and navigate to the servers list. */
  leaveServer() {
    this.openLeaveConfirm();
  }

  installRequiredServerPlugins(): void {
    void this.installServerRequirements(this.requiredPluginRequirements());
  }

  installOptionalServerPlugin(requirement: PluginRequirementSummary): void {
    void this.installServerRequirements([requirement]);
  }

  rejectOptionalServerPlugin(requirement: PluginRequirementSummary): void {
    this.pluginRequirements.dismissOptionalRequirement(requirement);
    this.pluginRequirementError.set(null);
  }

  hideOptionalServerPlugin(requirement: PluginRequirementSummary): void {
    this.pluginRequirements.dismissOptionalRequirement(requirement, { persist: true });
    this.pluginRequirementError.set(null);
  }

  /** Confirm the unified leave action and remove the server locally. */
  confirmLeave(result: { nextOwnerKey?: string }) {
    const roomId = this.currentRoom()?.id;

    this.showLeaveConfirm.set(false);

    if (!roomId)
      return;

    this.store.dispatch(RoomsActions.forgetRoom({
      roomId,
      nextOwnerKey: result.nextOwnerKey
    }));

    this.router.navigate(['/dashboard']);
  }

  /** Cancel the leave-server confirmation dialog. */
  cancelLeave() {
    this.showLeaveConfirm.set(false);
  }

  /** Close the server dropdown menu. */
  closeMenu() {
    this._showMenu.set(false);
  }

  private async installServerRequirements(requirements: PluginRequirementSummary[]): Promise<void> {
    const room = this.currentRoom();

    if (!room || requirements.length === 0 || this.pluginRequirementBusy()) {
      return;
    }

    this.pluginRequirementBusy.set(true);
    this.pluginRequirementError.set(null);

    try {
      await this.pluginStore.installServerRequirementsLocally(room.id, requirements, { activate: true });
    } catch (error) {
      this.pluginRequirementError.set(error instanceof Error ? error.message : this.appI18n.instant('shell.titleBar.pluginInstallFailed'));
    } finally {
      this.pluginRequirementBusy.set(false);
    }
  }

  /** Log out the current user, disconnect from signaling, and navigate to login. */
  logout() {
    this._showMenu.set(false);
    // Disconnect from signaling server - this broadcasts "user_left" to all
    // servers the user was a member of, so other users see them go offline.
    this.webrtc.disconnect();

    clearStoredCurrentUserId();
    this.store.dispatch(MessagesActions.clearMessages());
    this.store.dispatch(RoomsActions.resetRoomsState());
    this.store.dispatch(UsersActions.resetUsersState());

    this.router.navigate(['/login']);
  }

  private async copyInviteLink(inviteUrl: string): Promise<void> {
    if (navigator.clipboard?.writeText) {
      try {
        await navigator.clipboard.writeText(inviteUrl);
        return;
      } catch {}
    }

    const textarea = document.createElement('textarea');

    textarea.value = inviteUrl;
    textarea.setAttribute('readonly', 'true');
    textarea.style.position = 'fixed';
    textarea.style.opacity = '0';
    textarea.style.pointerEvents = 'none';
    document.body.appendChild(textarea);
    textarea.select();

    try {
      const copied = document.execCommand('copy');

      if (copied) {
        return;
      }
    } catch {
      /* fall through to prompt fallback */
    } finally {
      document.body.removeChild(textarea);
    }

    window.prompt(this.appI18n.instant('shell.titleBar.copyInvitePrompt'), inviteUrl);
  }

  private toSourceSelector(room: Room): { sourceId?: string; sourceUrl?: string } {
    return {
      sourceId: room.sourceId,
      sourceUrl: room.sourceUrl
    };
  }
}
