/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/features/tenantship/services/tenantInvitesRepository.js
|
| Repository de tenant_invites. Pure functions seguindo
| blueprints/repository-blueprint.md.
|
| A tabela tenant_invites já existe no schema (tenants_multi_tenant.sql:100)
| com role CHECK ['therapist','secretary'], token uuid auto, expires_at default
| now()+7d, accepted_at/by, revoked_at/by.
|--------------------------------------------------------------------------
*/
import { supabase } from '@/lib/supabase/client';
import { useTenantStore } from '@/stores/tenantStore';
import { assertTenantId, getUid, assertEmail, assertRole } from './_tenantGuards';
import { TENANT_INVITE_SELECT, flattenInviteRow } from './tenantInvitesSelects';

function resolveTenantId(tenantIdArg) {
    const tenantStore = useTenantStore();
    const tenantId = tenantIdArg || tenantStore.activeTenantId;
    assertTenantId(tenantId);
    return tenantId;
}

/**
 * Lista convites de um tenant. Ordem: pending primeiro (mais recentes), depois resto.
 *
 * @param {Object} [opts]
 * @param {string} [opts.tenantId]
 * @param {boolean} [opts.includeInactive=false] - se true, inclui revoked/accepted/expired
 */
export async function listForTenant({ tenantId, includeInactive = false } = {}) {
    const tid = resolveTenantId(tenantId);

    let q = supabase.from('tenant_invites').select(TENANT_INVITE_SELECT).eq('tenant_id', tid);

    if (!includeInactive) {
        q = q.is('accepted_at', null).is('revoked_at', null).gt('expires_at', new Date().toISOString());
    }

    q = q.order('created_at', { ascending: false });

    const { data, error } = await q;
    if (error) throw error;
    return (data || []).map(flattenInviteRow);
}

/**
 * Busca convite por token. Usado no fluxo de aceitar (read público).
 * NOTA: política RLS deve permitir SELECT por token sem auth — a ser configurada.
 *
 * @param {string} token  - uuid do convite
 */
export async function getByToken(token) {
    if (!token) throw new Error('Token inválido.');
    const { data, error } = await supabase.from('tenant_invites').select(TENANT_INVITE_SELECT).eq('token', token).maybeSingle();
    if (error) throw error;
    return data ? flattenInviteRow(data) : null;
}

/**
 * Envia novo convite (cria row). Idempotente por (tenant_id, email) ativo —
 * se já existe convite pending pro mesmo email, retorna o existente.
 *
 * TODO (Módulo 6 — Notificações): após criar a row, disparar email/WhatsApp
 * com o link `/aceitar-convite?token=${row.token}`. Hoje só insere.
 *
 * @param {Object} payload
 * @param {string} payload.email
 * @param {'therapist'|'secretary'} payload.role
 * @param {string} [payload.tenantId]
 * @returns {Promise<Object>} row do convite (novo ou existente)
 */
export async function sendInvite({ email, role, tenantId } = {}) {
    const tid = resolveTenantId(tenantId);
    const uid = await getUid();
    const safeEmail = assertEmail(email);
    const safeRole = assertRole(role);

    // Idempotência: se já existe pending pro mesmo (tenant, email), retorna existente
    const { data: existing } = await supabase
        .from('tenant_invites')
        .select(TENANT_INVITE_SELECT)
        .eq('tenant_id', tid)
        .eq('email', safeEmail)
        .is('accepted_at', null)
        .is('revoked_at', null)
        .gt('expires_at', new Date().toISOString())
        .maybeSingle();

    if (existing) return flattenInviteRow(existing);

    const { data, error } = await supabase
        .from('tenant_invites')
        .insert([{ tenant_id: tid, email: safeEmail, role: safeRole, invited_by: uid }])
        .select(TENANT_INVITE_SELECT)
        .single();

    if (error) throw error;
    return flattenInviteRow(data);
}

/**
 * Revoga convite (soft — registra revoked_at + revoked_by, não deleta a row).
 *
 * @param {string} inviteId
 * @param {Object} [opts]
 * @param {string} [opts.tenantId]
 */
export async function revokeInvite(inviteId, { tenantId } = {}) {
    if (!inviteId) throw new Error('ID inválido.');
    const tid = resolveTenantId(tenantId);
    const uid = await getUid();

    const { data, error } = await supabase
        .from('tenant_invites')
        .update({ revoked_at: new Date().toISOString(), revoked_by: uid })
        .eq('id', inviteId)
        .eq('tenant_id', tid)
        .select(TENANT_INVITE_SELECT)
        .single();

    if (error) throw error;
    return flattenInviteRow(data);
}

/**
 * Aceita convite — cria tenant_members + marca accepted_at no invite (atomicamente via RPC).
 *
 * RPC `accept_tenant_invite(p_token uuid)` (migration 20260520000005):
 *   - SECURITY DEFINER (auth.uid() do caller é o aceitador)
 *   - Lock FOR UPDATE no invite (anti-race)
 *   - Idempotente: re-aceitar não cria duplicata
 *   - Retorna jsonb { ok, tenant_id, role } em sucesso
 *   - Throw com mensagem PT-BR em erros (revogado, expirado, já aceito, sem sessão)
 *
 * @param {string} token  - uuid do invite
 * @returns {Promise<{ok: boolean, tenant_id: string, role: string}>}
 */
export async function acceptInvite(token) {
    if (!token) throw new Error('Token inválido.');

    const { data, error } = await supabase.rpc('accept_tenant_invite', { p_token: token });
    if (error) throw error;
    return data;
}
