/* eslint-disable @typescript-eslint/member-ordering */
import {
  CUSTOM_ELEMENTS_SCHEMA,
  Component,
  ElementRef,
  NgZone,
  computed,
  effect,
  inject,
  signal,
  viewChild
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideHash,
  lucideMonitor,
  lucideSettings,
  lucideUsers,
  lucideMenu,
  lucideX,
  lucideChevronLeft,
  lucidePhoneCall
} from '@ng-icons/lucide';

import { ChatMessagesComponent } from '../../../domains/chat/feature/chat-messages/chat-messages.component';
import { RoomsSidePanelComponent } from '../rooms-side-panel/rooms-side-panel.component';
import { VoiceWorkspaceComponent } from '../voice-workspace/voice-workspace.component';
import { ServersRailComponent } from '../../servers/servers-rail/servers-rail.component';

import {
  selectActiveChannelId,
  selectCurrentRoom,
  selectTextChannels
} from '../../../store/rooms/rooms.selectors';
import { SettingsModalService } from '../../../core/services/settings-modal.service';
import { ViewportService } from '../../../core/platform';
import { selectIsCurrentUserAdmin } from '../../../store/users/users.selectors';
import { VoiceWorkspaceService } from '../../../domains/voice-session';
import { ThemeNodeDirective, ThemeService } from '../../../domains/theme';
import { DirectCallService } from '../../../domains/direct-call';

/** Mobile-only page identifier within the chat-room view. */
export type ChatRoomMobilePage = 'channels' | 'main' | 'members';

const PAGE_TO_INDEX: Record<ChatRoomMobilePage, number> = {
  channels: 0,
  main: 1,
  members: 2
};
const INDEX_TO_PAGE: ChatRoomMobilePage[] = [
  'channels',
  'main',
  'members'
];

interface SwiperElement extends HTMLElement {
  swiper?: { activeIndex: number; slideTo: (index: number, speed?: number) => void };
}

@Component({
  selector: 'app-chat-room',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    ChatMessagesComponent,
    VoiceWorkspaceComponent,
    RoomsSidePanelComponent,
    ServersRailComponent,
    ThemeNodeDirective
  ],
  viewProviders: [
    provideIcons({
      lucideHash,
      lucideMonitor,
      lucideSettings,
      lucideUsers,
      lucideMenu,
      lucideX,
      lucideChevronLeft,
      lucidePhoneCall
    })
  ],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  templateUrl: './chat-room.component.html'
})
/**
 * Main chat room view combining the messages panel, side panels, and admin controls.
 *
 * On desktop the three panels (channels | main | members) are rendered side-by-side via the
 * theme-driven grid layout. On mobile the same panels are rendered as Swiper slides
 * (channels -> main -> members) so the user can swipe between them. `mobilePage`
 * remains the source of truth and stays in sync with the active slide.
 */
export class ChatRoomComponent {
  private readonly store = inject(Store);
  private readonly settingsModal = inject(SettingsModalService);
  private readonly theme = inject(ThemeService);
  private readonly viewport = inject(ViewportService);
  private readonly directCalls = inject(DirectCallService);
  private readonly zone = inject(NgZone);
  private voiceWorkspace = inject(VoiceWorkspaceService);
  private lastSeenChannelId: string | null = null;
  private lastSeenRoomId: string | null = null;
  private swiperListenerAttached: SwiperElement | null = null;
  showMenu = signal(false);
  showAdminPanel = signal(false);

  /** Active page within the mobile single-pane navigation flow. Ignored on desktop. */
  readonly mobilePage = signal<ChatRoomMobilePage>('channels');
  readonly isMobile = this.viewport.isMobile;
  readonly swiperRef = viewChild<ElementRef<SwiperElement>>('swiperEl');

  currentRoom = this.store.selectSignal(selectCurrentRoom);
  isAdmin = this.store.selectSignal(selectIsCurrentUserAdmin);
  textChannels = this.store.selectSignal(selectTextChannels);
  activeChannelId = this.store.selectSignal(selectActiveChannelId);
  /**
   * Resolved channel object for `activeChannelId`. Used on mobile to title the main pane
   * with the selected channel name instead of the room name.
   */
  activeChannel = computed(() => {
    const id = this.activeChannelId();

    if (!id) {
      return null;
    }

    return this.currentRoom()?.channels?.find((channel) => channel.id === id) ?? null;
  });
  isVoiceWorkspaceExpanded = this.voiceWorkspace.isExpanded;
  hasTextChannels = computed(() => this.textChannels().length > 0);
  activeCall = computed(() => {
    const currentSession = this.directCalls.currentSession();
    const visibleSessions = this.directCalls.visibleActiveSessions();

    return visibleSessions.find((session) => session.callId === currentSession?.callId) ?? visibleSessions[0] ?? null;
  });
  roomLayoutStyles = computed(() => this.theme.getLayoutContainerStyles('roomLayout'));
  channelsPanelLayoutStyles = computed(() => this.theme.getLayoutItemStyles('chatRoomChannelsPanel'));
  mainPanelLayoutStyles = computed(() => this.theme.getLayoutItemStyles('chatRoomMainPanel'));
  membersPanelLayoutStyles = computed(() => this.theme.getLayoutItemStyles('chatRoomMembersPanel'));

  constructor() {
    // When entering a server, always land on the channels list ("first page") on mobile, even
    // if a default channel is pre-selected. Once inside the server, *changing* channels
    // (i.e. user taps a channel in the list) advances to the main pane so the user sees the chat.
    effect(() => {
      const channelId = this.activeChannelId();
      const roomId = this.currentRoom()?.id ?? null;
      const isRoomChange = roomId !== this.lastSeenRoomId;

      this.lastSeenRoomId = roomId;

      if (!this.isMobile()) {
        this.lastSeenChannelId = channelId ?? null;
        return;
      }

      if (isRoomChange) {
        // New server: show the channels list and don't auto-advance.
        this.lastSeenChannelId = channelId ?? null;
        this.mobilePage.set('channels');
        return;
      }

      if (channelId && channelId !== this.lastSeenChannelId) {
        this.mobilePage.set('main');
      }

      this.lastSeenChannelId = channelId ?? null;
    });

    // Mirror `mobilePage` into the Swiper instance so back-button taps and the
    // channel-selected auto-advance actually slide the carousel.
    effect(() => {
      const el = this.swiperRef()?.nativeElement;
      const targetIndex = PAGE_TO_INDEX[this.mobilePage()];

      if (el?.swiper && el.swiper.activeIndex !== targetIndex) {
        el.swiper.slideTo(targetIndex);
      }
    });

    // Bridge Swiper's slidechange event back into `mobilePage`.
    effect((onCleanup) => {
      const el = this.swiperRef()?.nativeElement;

      if (!el || el === this.swiperListenerAttached) {
        return;
      }

      const handler = (event: Event) => {
        const detail = (event as CustomEvent).detail;
        const swiper = Array.isArray(detail) ? detail[0] : detail;
        const index = swiper?.activeIndex ?? 0;
        const page = INDEX_TO_PAGE[index] ?? 'channels';

        this.zone.run(() => this.mobilePage.set(page));
      };

      el.addEventListener('swiperslidechange', handler);
      this.swiperListenerAttached = el;

      onCleanup(() => {
        el.removeEventListener('swiperslidechange', handler);

        if (this.swiperListenerAttached === el) {
          this.swiperListenerAttached = null;
        }
      });
    });
  }

  /** Set the active mobile page. No-op on desktop. */
  setMobilePage(page: ChatRoomMobilePage): void {
    this.mobilePage.set(page);
  }

  openActiveCall(): void {
    const call = this.activeCall();

    if (call) {
      void this.directCalls.openCallView(call.callId);
    }
  }

  /** Open the settings modal to the Server admin page for the current room. */
  toggleAdminPanel() {
    const room = this.currentRoom();

    if (room) {
      this.settingsModal.open('server', room.id);
    }
  }
}

