import {
  Component,
  computed,
  effect,
  inject,
  OnDestroy,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { Store } from '@ngrx/store';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideCheck,
  lucideChevronDown,
  lucideGamepad2
} from '@ng-icons/lucide';
import { UserAvatarComponent } from '../user-avatar/user-avatar.component';
import { UserStatusService } from '../../../core/services/user-status.service';
import {
  GameActivity,
  User,
  UserStatus
} from '../../../shared-kernel';
import {
  EditableProfileAvatarSource,
  ProfileAvatarFacade,
  ProfileAvatarEditorService,
  PROFILE_AVATAR_ACCEPT_ATTRIBUTE,
  ProcessedProfileAvatar
} from '../../../domains/profile-avatar';
import { UsersActions } from '../../../store/users/users.actions';
import { selectUsersEntities } from '../../../store/users/users.selectors';
import { ThemeNodeDirective } from '../../../domains/theme';
import { formatGameActivityElapsed } from '../../../domains/game-activity';
import { ExternalLinkService } from '../../../core/platform/external-link.service';

@Component({
  selector: 'app-profile-card',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    UserAvatarComponent,
    ThemeNodeDirective
  ],
  viewProviders: [provideIcons({ lucideCheck, lucideChevronDown, lucideGamepad2 })],
  templateUrl: './profile-card.component.html'
})
export class ProfileCardComponent implements OnDestroy {
  readonly user = signal<User>({ id: '', oderId: '', username: '', displayName: '', status: 'offline', role: 'member', joinedAt: 0 });
  readonly displayedUser = computed(() => {
    const snapshot = this.user();
    const entities = this.users();
    const liveUser = entities[snapshot.id] ?? entities[snapshot.oderId];

    return liveUser ? { ...snapshot, ...liveUser } : snapshot;
  });
  readonly editable = signal(false);
  readonly showStatusMenu = signal(false);
  readonly avatarAccept = PROFILE_AVATAR_ACCEPT_ATTRIBUTE;
  readonly avatarError = signal<string | null>(null);
  readonly avatarSaving = signal(false);
  readonly editingField = signal<'displayName' | 'description' | null>(null);
  readonly displayNameDraft = signal('');
  readonly descriptionDraft = signal('');
  readonly activityNow = signal(Date.now());

  readonly statusOptions: { value: UserStatus | null; label: string; color: string }[] = [
    { value: null, label: 'Online', color: 'bg-green-500' },
    { value: 'away', label: 'Away', color: 'bg-yellow-500' },
    { value: 'busy', label: 'Do Not Disturb', color: 'bg-red-500' },
    { value: 'offline', label: 'Invisible', color: 'bg-gray-500' }
  ];

  private readonly store = inject(Store);
  private readonly users = this.store.selectSignal(selectUsersEntities);
  private readonly userStatus = inject(UserStatusService);
  private readonly profileAvatar = inject(ProfileAvatarFacade);
  private readonly profileAvatarEditor = inject(ProfileAvatarEditorService);
  private readonly externalLinks = inject(ExternalLinkService);
  private readonly activityTimer = setInterval(() => this.activityNow.set(Date.now()), 1_000);
  private readonly syncProfileDrafts = effect(
    () => {
      const user = this.displayedUser();
      const editingField = this.editingField();

      if (editingField !== 'displayName') {
        this.displayNameDraft.set(user.displayName || '');
      }

      if (editingField !== 'description') {
        this.descriptionDraft.set(user.description || '');
      }
    },
    { allowSignalWrites: true }
  );

  currentStatusColor(): string {
    switch (this.displayedUser().status) {
      case 'online':
        return 'bg-green-500';
      case 'away':
        return 'bg-yellow-500';
      case 'busy':
        return 'bg-red-500';
      case 'offline':
        return 'bg-gray-500';
      case 'disconnected':
        return 'bg-gray-500';
      default:
        return 'bg-green-500';
    }
  }

  currentStatusLabel(): string {
    switch (this.displayedUser().status) {
      case 'online':
        return 'Online';
      case 'away':
        return 'Away';
      case 'busy':
        return 'Do Not Disturb';
      case 'offline':
        return 'Invisible';
      case 'disconnected':
        return 'Offline';
      default:
        return 'Online';
    }
  }

  gameActivityElapsed(): string {
    const activity = this.displayedUser().gameActivity;

    return activity ? formatGameActivityElapsed(activity.startedAt, this.activityNow()) : '';
  }

  openGameStore(activity: GameActivity, event: Event): void {
    event.stopPropagation();

    if (activity.store?.url) {
      this.externalLinks.open(activity.store.url);
    }
  }

  ngOnDestroy(): void {
    clearInterval(this.activityTimer);
  }

  toggleStatusMenu(): void {
    this.showStatusMenu.update((isOpen) => !isOpen);
  }

  setStatus(status: UserStatus | null): void {
    this.userStatus.setManualStatus(status);
    this.showStatusMenu.set(false);
  }

  isStatusOptionSelected(status: UserStatus | null): boolean {
    const currentStatus = this.displayedUser().status;

    return status === null ? currentStatus === 'online' : currentStatus === status;
  }

  onDisplayNameInput(event: Event): void {
    this.displayNameDraft.set((event.target as HTMLInputElement).value);
  }

  onDescriptionInput(event: Event): void {
    this.descriptionDraft.set((event.target as HTMLTextAreaElement).value);
  }

  startEdit(field: 'displayName' | 'description'): void {
    if (!this.editable() || this.editingField() === field) {
      return;
    }

    this.editingField.set(field);
  }

  finishEdit(field: 'displayName' | 'description'): void {
    if (this.editingField() !== field) {
      return;
    }

    this.commitProfileDrafts();
    this.editingField.set(null);
  }

  pickAvatar(fileInput: HTMLInputElement): void {
    if (!this.editable() || this.avatarSaving()) {
      return;
    }

    this.avatarError.set(null);
    fileInput.click();
  }

  async onAvatarSelected(event: Event): Promise<void> {
    const input = event.target as HTMLInputElement;
    const file = input.files?.[0];

    let source: EditableProfileAvatarSource | null = null;

    input.value = '';

    if (!file) {
      return;
    }

    const validationError = this.profileAvatar.validateFile(file);

    if (validationError) {
      this.avatarError.set(validationError);
      return;
    }

    try {
      source = await this.profileAvatar.prepareEditableSource(file);
      const avatar = await this.profileAvatarEditor.open(source);

      if (!avatar) {
        return;
      }

      await this.applyAvatar(avatar);
    } catch {
      this.avatarError.set('Failed to open selected image.');
    } finally {
      this.profileAvatar.releaseEditableSource(source);
    }
  }

  async applyAvatar(avatar: ProcessedProfileAvatar): Promise<void> {
    const currentUser = this.displayedUser();

    this.avatarSaving.set(true);
    this.avatarError.set(null);

    try {
      await this.profileAvatar.persistProcessedAvatar(currentUser, avatar);

      const updates = this.profileAvatar.buildAvatarUpdates(avatar);

      this.store.dispatch(UsersActions.updateCurrentUserAvatar({ avatar: updates }));
      this.user.update((user) => ({
        ...user,
        ...updates
      }));
    } catch {
      this.avatarError.set('Failed to save profile image.');
    } finally {
      this.avatarSaving.set(false);
    }
  }

  private commitProfileDrafts(): void {
    if (!this.editable()) {
      return;
    }

    const displayName = this.normalizeDisplayName(this.displayNameDraft());

    if (!displayName) {
      this.displayNameDraft.set(this.user().displayName || '');
      return;
    }

    const user = this.displayedUser();
    const description = this.normalizeDescription(this.descriptionDraft());

    if (displayName === this.normalizeDisplayName(user.displayName) && description === this.normalizeDescription(user.description)) {
      return;
    }

    const profile = {
      displayName,
      description,
      profileUpdatedAt: Date.now()
    };

    this.store.dispatch(UsersActions.updateCurrentUserProfile({ profile }));
    this.user.update((user) => ({
      ...user,
      ...profile
    }));
  }

  private normalizeDisplayName(value: string | undefined): string {
    return value?.trim().replace(/\s+/g, ' ') || '';
  }

  private normalizeDescription(value: string | undefined): string | undefined {
    const normalized = value?.trim();

    return normalized || undefined;
  }
}
