/* eslint-disable @typescript-eslint/member-ordering */
import {
  Component,
  computed,
  effect,
  inject,
  input,
  output
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucidePhone,
  lucidePhoneCall,
  lucideTrash2
} from '@ng-icons/lucide';
import { map } from 'rxjs';
import { UserAvatarComponent } from '../../../../shared';
import { ThemeNodeDirective } from '../../../theme';
import { AttachmentFacade } from '../../../attachment';
import { DirectCallService } from '../../../direct-call';
import { selectAllUsers } from '../../../../store/users/users.selectors';
import type { DirectMessageConversation } from '../../domain/models/direct-message.model';
import type { Attachment } from '../../../attachment';
import type { User } from '../../../../shared-kernel';
import { DirectMessageService } from '../../application/services/direct-message.service';

@Component({
  selector: 'app-dm-conversation-item',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    UserAvatarComponent,
    ThemeNodeDirective
  ],
  viewProviders: [provideIcons({ lucidePhone, lucidePhoneCall, lucideTrash2 })],
  host: { class: 'block' },
  templateUrl: './dm-conversation-item.component.html'
})
export class DmConversationItemComponent {
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly store = inject(Store);
  private readonly attachments = inject(AttachmentFacade);
  private readonly directMessages = inject(DirectMessageService);
  private readonly directCalls = inject(DirectCallService);
  readonly conversation = input.required<DirectMessageConversation>();
  readonly conversationOpened = output<string>();
  readonly users = this.store.selectSignal(selectAllUsers);
  readonly routeConversationId = toSignal(this.route.paramMap.pipe(map((params) => params.get('conversationId'))), {
    initialValue: this.route.snapshot.paramMap.get('conversationId')
  });
  readonly isSelected = computed(() => this.routeConversationId() === this.conversation().id);
  readonly peerName = computed(() => this.resolvePeerName(this.conversation()));
  readonly peerAvatarUrl = computed(() => this.resolvePeerAvatarUrl(this.conversation()));
  readonly lastMessagePreview = computed(() => this.resolveLastMessagePreview(this.conversation()));
  readonly canCall = computed(() => this.canCallConversation(this.conversation()));
  readonly callIcon = computed(() => this.conversationCallIcon(this.conversation()));

  constructor() {
    effect(() => {
      const conversation = this.conversation();
      const peer = this.peerUser(conversation, this.users());

      if (!peer?.avatarUrl) {
        this.directMessages.requestPeerAvatarSync(conversation.id);
      }
    });
  }

  openConversation(): void {
    this.conversationOpened.emit(this.conversation().id);
    void this.router.navigate(['/dm', this.conversation().id]);
  }

  async forgetConversation(event: Event): Promise<void> {
    event.stopPropagation();
    const conversation = this.conversation();
    const conversations = this.directMessages.conversations();
    const nextConversation = conversations.find((entry) => entry.id !== conversation.id) ?? null;

    await this.directMessages.forgetConversation(conversation.id);

    if (this.routeConversationId() === conversation.id) {
      await this.router.navigate(nextConversation ? ['/dm', nextConversation.id] : ['/dm']);
    }
  }

  async callConversationPeer(event: Event): Promise<void> {
    event.stopPropagation();
    await this.directCalls.startConversationCall(this.conversation());
  }

  formatUnreadCount(count: number): string {
    return count > 99 ? '99+' : String(count);
  }

  private resolvePeerName(conversation: DirectMessageConversation): string {
    if (this.isGroupConversation(conversation)) {
      return conversation.title || this.groupConversationTitle(conversation);
    }

    const peerId = this.peerId(conversation);
    const knownUser = this.peerUser(conversation);

    return peerId ? knownUser?.displayName || conversation.participantProfiles[peerId]?.displayName || peerId : 'Direct Message';
  }

  private resolvePeerAvatarUrl(conversation: DirectMessageConversation): string | undefined {
    if (this.isGroupConversation(conversation)) {
      return undefined;
    }

    const peerId = this.peerId(conversation);
    const knownUser = this.peerUser(conversation);

    return peerId ? knownUser?.avatarUrl || conversation.participantProfiles[peerId]?.avatarUrl : undefined;
  }

  private resolveLastMessagePreview(conversation: DirectMessageConversation): string {
    const lastMessage = conversation.messages.at(-1);

    if (!lastMessage) {
      return 'No messages yet';
    }

    if (lastMessage.isDeleted) {
      return 'Message deleted';
    }

    if (this.isKlipyGif(lastMessage.content)) {
      return 'Sent a GIF';
    }

    this.attachments.updated();
    const attachments = this.attachments.getForMessage(lastMessage.id);

    if (attachments.length > 0) {
      return this.attachmentPreview(attachments);
    }

    return lastMessage.content || 'Attachment';
  }

  private conversationCallIcon(conversation: DirectMessageConversation): string {
    if (this.isGroupConversation(conversation)) {
      return this.directCalls.isCallingConversation(conversation.id) ? 'lucidePhoneCall' : 'lucidePhone';
    }

    const peer = this.peerUser(conversation);

    return peer && this.directCalls.isCallingUser(peer) ? 'lucidePhoneCall' : 'lucidePhone';
  }

  private canCallConversation(conversation: DirectMessageConversation): boolean {
    if (this.isGroupConversation(conversation)) {
      return conversation.participants.some((participantId) => participantId !== this.directMessages.currentUserId());
    }

    return !!this.peerUser(conversation);
  }

  private peerId(conversation: DirectMessageConversation): string | undefined {
    const currentUserId = this.directMessages.currentUserId();

    return conversation.participants.find((participantId) => participantId !== currentUserId);
  }

  private peerUser(conversation: DirectMessageConversation, users = this.users()): User | undefined {
    if (this.isGroupConversation(conversation)) {
      return undefined;
    }

    const peerId = this.peerId(conversation);

    return peerId ? users.find((user) => user.id === peerId || user.oderId === peerId) : undefined;
  }

  private isGroupConversation(conversation: DirectMessageConversation): boolean {
    return conversation.kind === 'group' || conversation.participants.length > 2;
  }

  private groupConversationTitle(conversation: DirectMessageConversation): string {
    const currentUserId = this.directMessages.currentUserId();
    const names = conversation.participants
      .filter((participantId) => participantId !== currentUserId)
      .map((participantId) => conversation.participantProfiles[participantId]?.displayName || participantId);

    if (names.length <= 3) {
      return names.join(', ');
    }

    return `${names.slice(0, 3).join(', ')} +${names.length - 3}`;
  }

  private isKlipyGif(content: string): boolean {
    return /!\[KLIPY GIF\]\([^)]*static\.klipy\.com[^)]*\)/i.test(content.trim());
  }

  private attachmentPreview(attachments: Attachment[]): string {
    if (attachments.some((attachment) => attachment.mime.startsWith('image/'))) {
      return 'Sent an image';
    }

    if (attachments.some((attachment) => attachment.mime.startsWith('video/'))) {
      return 'Sent a video';
    }

    if (attachments.some((attachment) => attachment.mime.startsWith('audio/'))) {
      return 'Sent audio';
    }

    return attachments.length === 1 ? 'Sent an attachment' : 'Sent attachments';
  }
}
