/**
 * View-as fidelity guards. RLS only enforces visibility for the real
 * JWT user; when a super-admin is viewing-as someone else, the
 * underlying queries still run as the super-admin. These helpers
 * mirror the would-be RLS rules at the application layer so that
 * mutations can't reach rows the effective user couldn't see.
 *
 * Each `guard*` returns an error message string if the operation
 * should be denied, or null to allow. Callers call their own
 * `fail(msg)` (which redirects with an error param) on a non-null
 * result. This keeps the redirect URL action-specific.
 *
 * Usage:
 *   const violation = await guardEntry(supabase, eu, org.id, id)
 *   if (violation) fail(violation, id)
 */

import type { SupabaseClient } from '@supabase/supabase-js'
import {
  getViewAsImportScope,
  type EffectiveUser,
} from '@/lib/effective-user'

const OUT_OF_VIEW_AS_SCOPE =
  "That row isn't visible to the user you're viewing as. Exit view-as to make this change."

/**
 * Guard a single entry id: the entry must exist in the org and its
 * import must be in the effective user's view-as scope.
 */
export async function guardEntry(
  supabase: SupabaseClient,
  eu: EffectiveUser | null,
  orgId: string,
  entryId: string,
): Promise<string | null> {
  const scope = await getViewAsImportScope(supabase, orgId, eu)
  if (!scope) return null
  const { data } = await supabase
    .from('time_entries')
    .select('import_id')
    .eq('org_id', orgId)
    .eq('id', entryId)
    .maybeSingle<{ import_id: string | null }>()
  if (!data) return 'Entry not found.'
  if (!data.import_id || !scope.includes(data.import_id)) {
    return OUT_OF_VIEW_AS_SCOPE
  }
  return null
}

/**
 * Guard a batch of entry ids. Fails if ANY id is out of scope —
 * the safer behavior than silently filtering, because the caller
 * is asserting a specific count of rows.
 */
export async function guardEntryIds(
  supabase: SupabaseClient,
  eu: EffectiveUser | null,
  orgId: string,
  entryIds: string[],
): Promise<string | null> {
  if (entryIds.length === 0) return null
  const scope = await getViewAsImportScope(supabase, orgId, eu)
  if (!scope) return null
  const { data } = await supabase
    .from('time_entries')
    .select('id, import_id')
    .eq('org_id', orgId)
    .in('id', entryIds)
    .returns<{ id: string; import_id: string | null }[]>()
  const rows = data ?? []
  if (rows.length !== entryIds.length) return 'Some selected entries were not found.'
  for (const r of rows) {
    if (!r.import_id || !scope.includes(r.import_id)) {
      return OUT_OF_VIEW_AS_SCOPE
    }
  }
  return null
}

/**
 * Guard an `clockify_imports` row id: must be in the effective user's
 * view-as scope.
 */
export async function guardImport(
  supabase: SupabaseClient,
  eu: EffectiveUser | null,
  orgId: string,
  importId: string,
): Promise<string | null> {
  const scope = await getViewAsImportScope(supabase, orgId, eu)
  if (!scope) return null
  if (!scope.includes(importId)) return OUT_OF_VIEW_AS_SCOPE
  return null
}

/**
 * Guard a `teams` row id: in view-as as a non-owner, must be owned
 * by the effective user. Outside view-as: no extra check.
 */
export async function guardTeam(
  supabase: SupabaseClient,
  eu: EffectiveUser | null,
  orgId: string,
  teamId: string,
): Promise<string | null> {
  if (!eu?.is_viewing_as) return null
  if (eu.is_super_admin || eu.org_role === 'owner') return null
  const { data } = await supabase
    .from('teams')
    .select('owner_user_id')
    .eq('id', teamId)
    .eq('org_id', orgId)
    .maybeSingle<{ owner_user_id: string }>()
  if (!data) return 'Team not found.'
  if (data.owner_user_id !== eu.effective_user_id) return OUT_OF_VIEW_AS_SCOPE
  return null
}

/**
 * Guard a `team_members` row id: in view-as as a non-owner, the
 * member's team must be owned by the effective user.
 */
export async function guardTeamMember(
  supabase: SupabaseClient,
  eu: EffectiveUser | null,
  orgId: string,
  memberId: string,
): Promise<string | null> {
  if (!eu?.is_viewing_as) return null
  if (eu.is_super_admin || eu.org_role === 'owner') return null
  const { data } = await supabase
    .from('team_members')
    .select('team:teams(owner_user_id)')
    .eq('id', memberId)
    .eq('org_id', orgId)
    .maybeSingle<{ team: { owner_user_id: string } | null }>()
  if (!data?.team) return 'Team member not found.'
  if (data.team.owner_user_id !== eu.effective_user_id) return OUT_OF_VIEW_AS_SCOPE
  return null
}
