import { HttpStatus } from '@nestjs/common';
import { Not } from 'typeorm';
import { createGeneralError } from '../../utils';
import {
  PermissionValue,
} from '../../modules/access-control/permission-catalog';
import { PermissionUserContext } from './permission-context.types';

export type DataScopeMode = 'own' | 'all';
export type EffectiveDataFilter = 'own' | 'all_others' | 'org';

export function assertToggle(
  permissions: Record<string, PermissionValue>,
  key: string,
) {
  if (permissions[key] !== 'true') {
    throw createGeneralError(
      'You do not have permission to perform this action',
      key,
      HttpStatus.FORBIDDEN,
    );
  }
}

export function assertScope(
  permissions: Record<string, PermissionValue>,
  key: string,
): Exclude<PermissionValue, 'true' | 'false'> {
  const value = permissions[key];

  if (
    value === 'none' ||
    value === undefined ||
    value === 'true' ||
    value === 'false'
  ) {
    throw createGeneralError(
      'You do not have permission to access this resource',
      key,
      HttpStatus.FORBIDDEN,
    );
  }

  return value;
}

export function resolveDataScope(
  permissions: Record<string, PermissionValue>,
  viewKey: string,
  requested?: DataScopeMode,
  ctx?: PermissionUserContext,
): EffectiveDataFilter {
  const scope = assertScope(permissions, viewKey);

  if (ctx?.role === 'owner') {
    return 'org';
  }

  if (scope === 'own') {
    if (requested === 'all') {
      throw createGeneralError(
        'All members data scope is not allowed for this permission',
        viewKey,
        HttpStatus.FORBIDDEN,
      );
    }
    return 'own';
  }

  if (scope === 'all') {
    if (requested === 'own') {
      throw createGeneralError(
        'Own data scope is not allowed for this permission',
        viewKey,
        HttpStatus.FORBIDDEN,
      );
    }
    return 'org';
  }

  if (scope === 'own_and_all') {
    const mode = requested ?? 'own';
    return mode === 'all' ? 'org' : 'own';
  }

  throw createGeneralError(
    'You do not have permission to access this resource',
    viewKey,
    HttpStatus.FORBIDDEN,
  );
}

export function buildMemberFilter(
  filter: EffectiveDataFilter,
  actorMemberId: string,
): Record<string, unknown> {
  if (filter === 'own') {
    return { member_id: actorMemberId };
  }

  if (filter === 'all_others') {
    return { member_id: Not(actorMemberId) };
  }

  return {};
}

export function buildMemberEntityFilter(
  filter: EffectiveDataFilter,
  actorMemberId: string,
): Record<string, unknown> {
  if (filter === 'own') {
    return { id: actorMemberId };
  }

  if (filter === 'all_others') {
    return { id: Not(actorMemberId) };
  }

  return {};
}

export function canPerformScopedAction(
  permissions: Record<string, PermissionValue>,
  actionKey: string,
  targetMemberId: string,
  actorMemberId: string,
): boolean {
  const scope = permissions[actionKey];

  if (scope === 'none' || scope === undefined || scope === 'true' || scope === 'false') {
    return false;
  }

  if (scope === 'own') {
    return targetMemberId === actorMemberId;
  }

  if (scope === 'all') {
    return targetMemberId !== actorMemberId;
  }

  if (scope === 'own_and_all') {
    return true;
  }

  return false;
}

export function assertScopedAction(
  permissions: Record<string, PermissionValue>,
  actionKey: string,
  targetMemberId: string,
  actorMemberId: string,
) {
  if (
    !canPerformScopedAction(
      permissions,
      actionKey,
      targetMemberId,
      actorMemberId,
    )
  ) {
    throw createGeneralError(
      'You do not have permission to perform this action',
      actionKey,
      HttpStatus.FORBIDDEN,
    );
  }
}

export function isSelfMemberRequest(
  ctx: PermissionUserContext,
  targetMemberId: string,
): boolean {
  return ctx.role !== 'owner' && ctx.sub === targetMemberId;
}

const SELF_MEMBER_BLOCKED_UPDATE_FIELDS = [
  'access_role_id',
  'role',
  'status',
  'employee_id',
  'department_id',
  'designation_id',
  'location_id',
  'employment_type_id',
  'hiring_source_id',
  'reporting_manager_id',
  'date_of_joining',
] as const;

export function stripSelfMemberUpdateFields<T extends Record<string, unknown>>(
  data: T,
): T {
  const stripped = { ...data };

  for (const field of SELF_MEMBER_BLOCKED_UPDATE_FIELDS) {
    delete stripped[field];
  }

  return stripped;
}

export function assertGeneralModuleAccess(
  permissions: Record<string, PermissionValue>,
  manage = false,
) {
  assertToggle(permissions, 'general.access');
  if (manage) {
    assertToggle(permissions, 'general.manage');
  }
}

export function assertSettingsModuleAccess(
  permissions: Record<string, PermissionValue>,
  manage = false,
) {
  assertToggle(permissions, 'settings.access');
  if (manage) {
    assertToggle(permissions, 'settings.manage');
  }
}

export function assertPermissionsModuleAccess(
  permissions: Record<string, PermissionValue>,
  manage = false,
) {
  assertToggle(permissions, 'permissions.access');
  if (manage) {
    assertToggle(permissions, 'permissions.manage');
  }
}

export function assertRecruitmentModuleAccess(
  permissions: Record<string, PermissionValue>,
  options: { manage?: boolean; apiKeys?: boolean } = {},
) {
  assertToggle(permissions, 'recruitment.access');
  if (options.manage) {
    assertToggle(permissions, 'recruitment.manage');
  }
  if (options.apiKeys) {
    assertToggle(permissions, 'recruitment.api_keys');
  }
}

export function assertOnboardingModuleAccess(
  permissions: Record<string, PermissionValue>,
  options: { manage?: boolean; convert?: boolean } = {},
) {
  assertToggle(permissions, 'onboarding.access');
  if (options.manage) {
    assertToggle(permissions, 'onboarding.manage');
  }
  if (options.convert) {
    assertToggle(permissions, 'onboarding.convert_member');
  }
}
