/* eslint-disable @typescript-eslint/member-ordering */
import {
  Component,
  computed,
  effect,
  inject,
  input
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { Store } from '@ngrx/store';
import {
  lucideArrowDown,
  lucideArrowUp,
  lucideCheck,
  lucidePlus,
  lucideTrash2
} from '@ng-icons/lucide';

import {
  ChannelPermissionOverride,
  PermissionState,
  Room,
  RoomPermissionKey,
  RoomRole
} from '../../../../shared-kernel';
import { selectCurrentUser } from '../../../../store/users/users.selectors';
import { RoomsActions } from '../../../../store/rooms/rooms.actions';
import {
  canManageRole,
  createCustomRoomRole,
  normalizeRoomAccessControl,
  removeRole,
  reorderRoles,
  resolveRoomPermission,
  ROOM_PERMISSION_DEFINITIONS,
  sortRolesForDisplay,
  withUpdatedRole
} from '../../../../domains/access-control';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';
import { SelectOnFocusDirective, SubmitOnEnterDirective } from '../../../../shared/directives';

function upsertRoleChannelOverride(
  overrides: readonly ChannelPermissionOverride[] | undefined,
  channelId: string,
  roleId: string,
  permission: RoomPermissionKey,
  value: PermissionState
): ChannelPermissionOverride[] {
  const filteredOverrides = (overrides ?? []).filter(
    (override) =>
      !(override.channelId === channelId && override.targetType === 'role' && override.targetId === roleId && override.permission === permission)
  );

  if (value === 'inherit') {
    return filteredOverrides;
  }

  return [
    ...filteredOverrides,
    {
      channelId,
      targetType: 'role',
      targetId: roleId,
      permission,
      value
    }
  ];
}

@Component({
  selector: 'app-permissions-settings',
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    NgIcon,
    SelectOnFocusDirective,
    SubmitOnEnterDirective,
    ...APP_TRANSLATE_IMPORTS
  ],
  viewProviders: [
    provideIcons({
      lucideArrowDown,
      lucideArrowUp,
      lucideCheck,
      lucidePlus,
      lucideTrash2
    })
  ],
  templateUrl: './permissions-settings.component.html'
})
export class PermissionsSettingsComponent {
  private store = inject(Store);
  private readonly appI18n = inject(AppI18nService);

  server = input<Room | null>(null);
  isAdmin = input(false);
  currentUser = this.store.selectSignal(selectCurrentUser);
  permissionDefinitions = computed(() =>
    ROOM_PERMISSION_DEFINITIONS.map((definition) => ({
      key: definition.key,
      label: this.appI18n.instant(`permissions.${definition.key}.label`),
      description: this.appI18n.instant(`permissions.${definition.key}.description`)
    }))
  );
  permissionStates: PermissionState[] = [
    'inherit',
    'allow',
    'deny'
  ];
  normalizedServer = computed(() => {
    const room = this.server();

    return room ? normalizeRoomAccessControl(room) : null;
  });
  roles = computed<RoomRole[]>(() => sortRolesForDisplay(this.normalizedServer()?.roles ?? []));
  channels = computed(() => this.normalizedServer()?.channels ?? []);
  canManageRoles = computed(() => {
    const room = this.normalizedServer();
    const user = this.currentUser();

    return !!room && !!user && (room.hostId === user.id || room.hostId === user.oderId || resolveRoomPermission(room, user, 'manageRoles'));
  });
  canManageServer = computed(() => {
    const room = this.normalizedServer();
    const user = this.currentUser();

    return !!room && !!user && (room.hostId === user.id || room.hostId === user.oderId || resolveRoomPermission(room, user, 'manageServer'));
  });

  selectedRoleKey: string | null = null;
  selectedChannelKey = '';
  roleName = '';
  roleColor = '#94a3b8';

  constructor() {
    effect(() => {
      const room = this.normalizedServer();
      const roles = this.roles();
      const channels = this.channels();

      if (!room || roles.length === 0) {
        this.selectedRoleKey = null;
        this.selectedChannelKey = '';
        this.roleName = '';
        this.roleColor = '#94a3b8';
        return;
      }

      if (!this.selectedRoleKey || !roles.some((role) => role.id === this.selectedRoleKey)) {
        this.selectedRoleKey = roles[0]?.id ?? null;
      }

      if (!this.selectedChannelKey || !channels.some((channel) => channel.id === this.selectedChannelKey)) {
        this.selectedChannelKey = channels[0]?.id ?? '';
      }

      const selectedRole = roles.find((role) => role.id === this.selectedRoleKey) ?? null;

      this.roleName = selectedRole?.name ?? '';
      this.roleColor = selectedRole?.color ?? '#94a3b8';
    });
  }

  loadPermissions(room: Room): void {
    const normalizedRoom = normalizeRoomAccessControl(room);

    this.selectedRoleKey = sortRolesForDisplay(normalizedRoom.roles ?? [])[0]?.id ?? null;
    this.selectedChannelKey = normalizedRoom.channels?.[0]?.id ?? '';
  }

  selectedRole(): RoomRole | null {
    return this.roles().find((role) => role.id === this.selectedRoleKey) ?? null;
  }

  selectedChannel() {
    return this.channels().find((channel) => channel.id === this.selectedChannelKey) ?? null;
  }

  canEditSelectedRole(): boolean {
    const room = this.normalizedServer();
    const user = this.currentUser();
    const role = this.selectedRole();

    return !!room && !!user && !!role && canManageRole(room, user, role.id);
  }

  canEditSelectedRoleMetadata(): boolean {
    const role = this.selectedRole();

    return !!role && !role.isSystem && this.canEditSelectedRole();
  }

  selectRole(roleId: string): void {
    this.selectedRoleKey = roleId;
  }

  selectChannel(channelId: string): void {
    this.selectedChannelKey = channelId;
  }

  createRole(): void {
    const room = this.normalizedServer();

    if (!room || !this.canManageRoles())
      return;

    const role = createCustomRoomRole(this.appI18n.instant('settings.permissions.newRole'), room.roles ?? []);

    this.selectedRoleKey = role.id;
    this.roleName = role.name;
    this.roleColor = role.color ?? '#94a3b8';

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: { roles: [...(room.roles ?? []), role] }
      })
    );
  }

  saveRoleDetails(): void {
    const room = this.normalizedServer();
    const role = this.selectedRole();

    if (!room || !role || !this.canEditSelectedRoleMetadata())
      return;

    const roles = withUpdatedRole(room.roles ?? [], role.id, {
      name: this.roleName.trim() || role.name,
      color: this.roleColor.trim() || undefined
    });

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: { roles }
      })
    );
  }

  coercePermissionState(value: string): PermissionState {
    return value === 'allow' || value === 'deny' || value === 'inherit' ? value : 'inherit';
  }

  permissionStateLabel(state: PermissionState): string {
    return this.appI18n.instant(`settings.permissions.states.${state}`);
  }

  slowModeValue(interval: number | undefined): string {
    return String(interval ?? 0);
  }

  canMoveSelectedRoleUp(): boolean {
    const role = this.selectedRole();

    if (!role || role.isSystem || !this.canEditSelectedRoleMetadata()) {
      return false;
    }

    return (
      this.roles()
        .filter((candidateRole) => !candidateRole.isSystem)
        .findIndex((candidateRole) => candidateRole.id === role.id) > 0
    );
  }

  canMoveSelectedRoleDown(): boolean {
    const role = this.selectedRole();
    const customRoles = this.roles().filter((candidateRole) => !candidateRole.isSystem);

    if (!role || role.isSystem || !this.canEditSelectedRoleMetadata()) {
      return false;
    }

    const index = customRoles.findIndex((candidateRole) => candidateRole.id === role.id);

    return index >= 0 && index < customRoles.length - 1;
  }

  moveSelectedRole(direction: 'up' | 'down'): void {
    const room = this.normalizedServer();
    const role = this.selectedRole();

    if (!room || !role || !this.canEditSelectedRoleMetadata())
      return;

    const orderedRoleIds = this.roles()
      .filter((candidateRole) => !candidateRole.isSystem)
      .map((candidateRole) => candidateRole.id);
    const currentIndex = orderedRoleIds.findIndex((candidateRoleId) => candidateRoleId === role.id);
    const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;

    if (currentIndex < 0 || targetIndex < 0 || targetIndex >= orderedRoleIds.length)
      return;

    const nextOrderedRoleIds = [...orderedRoleIds];

    [nextOrderedRoleIds[currentIndex], nextOrderedRoleIds[targetIndex]] = [nextOrderedRoleIds[targetIndex], nextOrderedRoleIds[currentIndex]];

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: { roles: reorderRoles(room.roles ?? [], nextOrderedRoleIds) }
      })
    );
  }

  deleteSelectedRole(): void {
    const room = this.normalizedServer();
    const role = this.selectedRole();

    if (!room || !role || !this.canEditSelectedRoleMetadata())
      return;

    const nextState = removeRole(room.roles ?? [], room.roleAssignments, role.id);

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: {
          roles: nextState.roles,
          roleAssignments: nextState.roleAssignments
        }
      })
    );
  }

  updateSlowMode(intervalValue: string): void {
    const room = this.normalizedServer();

    if (!room || !this.canManageServer())
      return;

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: { slowModeInterval: Number(intervalValue) || 0 }
      })
    );
  }

  permissionState(permission: RoomPermissionKey): PermissionState {
    return this.selectedRole()?.permissions?.[permission] ?? 'inherit';
  }

  setSelectedRolePermission(permission: RoomPermissionKey, value: PermissionState): void {
    const room = this.normalizedServer();
    const role = this.selectedRole();

    if (!room || !role || !this.canEditSelectedRole())
      return;

    const roles = withUpdatedRole(room.roles ?? [], role.id, {
      permissions: {
        ...role.permissions,
        [permission]: value
      }
    });

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: { roles }
      })
    );
  }

  channelOverrideState(permission: RoomPermissionKey): PermissionState {
    const room = this.normalizedServer();
    const role = this.selectedRole();
    const channel = this.selectedChannel();

    if (!room || !role || !channel) {
      return 'inherit';
    }

    return (
      room.channelPermissions?.find(
        (override) =>
          override.channelId === channel.id && override.targetType === 'role' && override.targetId === role.id && override.permission === permission
      )?.value ?? 'inherit'
    );
  }

  setChannelOverride(permission: RoomPermissionKey, value: PermissionState): void {
    const room = this.normalizedServer();
    const role = this.selectedRole();
    const channel = this.selectedChannel();

    if (!room || !role || !channel || !this.canEditSelectedRole())
      return;

    this.store.dispatch(
      RoomsActions.updateRoomAccessControl({
        roomId: room.id,
        changes: {
          channelPermissions: upsertRoleChannelOverride(room.channelPermissions, channel.id, role.id, permission, value)
        }
      })
    );
  }

  trackRole(_: number, role: RoomRole): string {
    return role.id;
  }
}
