import { getTenantFieldRules } from '@zync/db/queries'

type FieldPermission = 'visible' | 'read_only' | 'hidden'

type Rule = {
  entityType: string
  fieldName: string
  role: string
  permission: string
}

type SessionLike = {
  tid?: string | null
  role?: string | null
}

function resolveEntityRules(
  rules: Rule[],
  entityType: string,
  role: string | null | undefined,
): Map<string, FieldPermission> {
  const effective = new Map<string, FieldPermission>()
  for (const rule of rules) {
    if (rule.entityType !== entityType || rule.role !== (role ?? '')) continue
    if (rule.permission === 'visible') continue
    effective.set(rule.fieldName, rule.permission as FieldPermission)
  }
  return effective
}

export function applyFieldPermissions<T extends object>(
  entity: T,
  entityType: string,
  role: string | null | undefined,
  rules: Rule[],
): { data: T; readOnly: string[] } {
  const next = { ...entity } as T
  const mutable = next as Record<string, unknown>
  const readOnly = new Set<string>()
  const effective = resolveEntityRules(rules, entityType, role)

  for (const [fieldName, permission] of effective.entries()) {
    if (permission === 'hidden') {
      delete mutable[fieldName]
      continue
    }
    if (permission === 'read_only' && fieldName in mutable) {
      readOnly.add(fieldName)
    }
  }

  return { data: next, readOnly: Array.from(readOnly) }
}

export function attachReadOnlyMeta<T extends object>(
  entity: T,
  readOnly: string[],
): T & { _meta: { readOnly: string[] } } {
  return {
    ...entity,
    _meta: {
      readOnly,
    },
  }
}

export function applyFieldPermissionsToPage<
  T extends object,
  P extends { items: T[] },
>(
  page: P,
  entityType: string,
  role: string | null | undefined,
  rules: Rule[],
): P & { _meta: { readOnly: string[] } } {
  const readOnly = resolveEntityRules(rules, entityType, role)
  const readOnlyFields = Array.from(readOnly.entries())
    .filter(([, permission]) => permission === 'read_only')
    .map(([fieldName]) => fieldName)

  return {
    ...page,
    items: page.items.map((item) => applyFieldPermissions(item, entityType, role, rules).data),
    _meta: {
      readOnly: readOnlyFields,
    },
  }
}

export async function getFieldPermissionContext(
  db: unknown,
  session: SessionLike,
): Promise<{ role: string; rules: Rule[] }> {
  if (!session.tid) {
    return { role: session.role ?? '', rules: [] }
  }

  const rules = await getTenantFieldRules(db as Parameters<typeof getTenantFieldRules>[0], session.tid)
  return {
    role: session.role ?? '',
    rules,
  }
}
