/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/services/Documents.service.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import { supabase } from '@/lib/supabase/client';

import { tenantDb } from '@/lib/supabase/tenantClient';
const BUCKET = 'documents';

// ── Helpers ──────────────────────────────────────────────────

async function getOwnerId() {
    const { data, error } = await supabase.auth.getUser();
    if (error) throw error;
    const uid = data?.user?.id;
    if (!uid) throw new Error('Sessão inválida.');
    return uid;
}

async function getActiveTenantId(uid) {
    const { data, error } = await supabase
        .from('tenant_members')
        .select('tenant_id')
        .eq('user_id', uid)
        .eq('status', 'active')
        .order('created_at', { ascending: false })
        .limit(1)
        .single();
    if (error) throw error;
    if (!data?.tenant_id) throw new Error('Tenant não encontrado.');
    return data.tenant_id;
}

function buildStoragePath(tenantId, patientId, fileName) {
    const timestamp = Date.now();
    const safe = String(fileName || 'arquivo').replace(/[^a-zA-Z0-9._-]/g, '_');
    return `${tenantId}/${patientId}/${timestamp}-${safe}`;
}

// V#51: SHA-256 hex do conteúdo. Calculado no client antes do upload.
async function computeSha256Hex(file) {
    if (!file || !window?.crypto?.subtle) return null;
    try {
        const buf = await file.arrayBuffer();
        const hashBuf = await crypto.subtle.digest('SHA-256', buf);
        return Array.from(new Uint8Array(hashBuf))
            .map((b) => b.toString(16).padStart(2, '0'))
            .join('');
    } catch {
        return null; // hash é best-effort; falha não bloqueia upload
    }
}

// ── Upload ──────────────────────────────────────────────────

/**
 * Faz upload de arquivo ao Storage e registra na tabela documents.
 *
 * @param {File} file           - Objeto File do input
 * @param {string} patientId    - UUID do paciente
 * @param {object} meta         - { tipo_documento, categoria, descricao, tags[], agenda_evento_id, visibilidade }
 * @returns {object}            - Registro criado em documents
 */
export async function uploadDocument(file, patientId, meta = {}) {
    if (!file) throw new Error('Nenhum arquivo selecionado.');
    if (!patientId) throw new Error('Paciente não informado.');

    const ownerId = await getOwnerId();
    const tenantId = await getActiveTenantId(ownerId);

    // V#51: hash SHA-256 do conteúdo ANTES do upload (integridade)
    const contentSha256 = await computeSha256Hex(file);

    // Upload ao Storage
    const path = buildStoragePath(tenantId, patientId, file.name);
    const { error: upErr } = await supabase.storage
        .from(BUCKET)
        .upload(path, file, { contentType: file.type });

    if (upErr) throw upErr;

    // Insert na tabela
    const row = {
        owner_id: ownerId,
        patient_id: patientId,
        bucket_path: path,
        storage_bucket: BUCKET,
        nome_original: file.name,
        mime_type: file.type || null,
        tamanho_bytes: file.size || null,
        content_sha256: contentSha256,
        tipo_documento: meta.tipo_documento || 'outro',
        categoria: meta.categoria || null,
        descricao: meta.descricao || null,
        tags: meta.tags || [],
        agenda_evento_id: meta.agenda_evento_id || null,
        visibilidade: meta.visibilidade || 'privado',
        compartilhado_portal: meta.compartilhado_portal || false,
        compartilhado_supervisor: meta.compartilhado_supervisor || false,
        enviado_pelo_paciente: meta.enviado_pelo_paciente || false,
        status_revisao: meta.enviado_pelo_paciente ? 'pendente' : 'aprovado',
        uploaded_by: ownerId
    };

    const { data, error } = await tenantDb().from('documents')
        .insert(row)
        .select('*')
        .single();

    if (error) {
        // Tenta limpar o arquivo do Storage em caso de erro no insert
        await supabase.storage.from(BUCKET).remove([path]).catch(() => {});
        throw error;
    }

    return data;
}

// ── List ────────────────────────────────────────────────────

/**
 * Lista documentos de um paciente (excluindo soft-deleted).
 *
 * @param {string} patientId
 * @param {object} filters - { tipo_documento, categoria, tag, search }
 */
export async function listDocuments(patientId, filters = {}) {
    const ownerId = await getOwnerId();

    let query = tenantDb().from('documents')
        .select('*')
        .eq('owner_id', ownerId)
        .eq('patient_id', patientId)
        .is('deleted_at', null)
        .order('uploaded_at', { ascending: false });

    if (filters.tipo_documento) {
        query = query.eq('tipo_documento', filters.tipo_documento);
    }
    if (filters.categoria) {
        query = query.eq('categoria', filters.categoria);
    }
    if (filters.tag) {
        query = query.contains('tags', [filters.tag]);
    }
    if (filters.search) {
        query = query.ilike('nome_original', `%${filters.search}%`);
    }

    const { data, error } = await query;
    if (error) throw error;
    return data || [];
}

/**
 * Lista todos os documentos do owner (todos os pacientes).
 */
export async function listAllDocuments(filters = {}) {
    const ownerId = await getOwnerId();

    let query = tenantDb().from('documents')
        .select('*, patients!inner(nome_completo)')
        .eq('owner_id', ownerId)
        .is('deleted_at', null)
        .order('uploaded_at', { ascending: false });

    if (filters.tipo_documento) {
        query = query.eq('tipo_documento', filters.tipo_documento);
    }
    if (filters.search) {
        query = query.ilike('nome_original', `%${filters.search}%`);
    }

    const { data, error } = await query;
    if (error) throw error;
    return data || [];
}

// ── Get one ─────────────────────────────────────────────────

export async function getDocument(id) {
    const ownerId = await getOwnerId();

    const { data, error } = await tenantDb().from('documents')
        .select('*')
        .eq('id', id)
        .eq('owner_id', ownerId)
        .single();

    if (error) throw error;
    return data;
}

// ── Update ──────────────────────────────────────────────────

export async function updateDocument(id, payload) {
    const ownerId = await getOwnerId();
    if (!id) throw new Error('ID inválido.');

    const row = {};
    if (payload.tipo_documento !== undefined) row.tipo_documento = payload.tipo_documento;
    if (payload.categoria !== undefined) row.categoria = payload.categoria;
    if (payload.descricao !== undefined) row.descricao = payload.descricao;
    if (payload.tags !== undefined) row.tags = payload.tags;
    if (payload.visibilidade !== undefined) row.visibilidade = payload.visibilidade;
    if (payload.compartilhado_portal !== undefined) row.compartilhado_portal = payload.compartilhado_portal;
    if (payload.compartilhado_supervisor !== undefined) row.compartilhado_supervisor = payload.compartilhado_supervisor;
    if (payload.status_revisao !== undefined) {
        row.status_revisao = payload.status_revisao;
        row.revisado_por = ownerId;
        row.revisado_em = new Date().toISOString();
    }

    row.updated_at = new Date().toISOString();

    const { data, error } = await tenantDb().from('documents')
        .update(row)
        .eq('id', id)
        .eq('owner_id', ownerId)
        .select('*')
        .single();

    if (error) throw error;
    return data;
}

// ── Soft Delete ─────────────────────────────────────────────

/**
 * Soft delete com retencao. O arquivo permanece no Storage.
 * retencaoAnos: numero de anos de retencao (padrao 5 — CFP).
 */
export async function softDeleteDocument(id, retencaoAnos = 5) {
    const ownerId = await getOwnerId();
    if (!id) throw new Error('ID inválido.');

    const retencaoAte = new Date();
    retencaoAte.setFullYear(retencaoAte.getFullYear() + retencaoAnos);

    const { error } = await tenantDb().from('documents')
        .update({
            deleted_at: new Date().toISOString(),
            deleted_by: ownerId,
            retencao_ate: retencaoAte.toISOString(),
            updated_at: new Date().toISOString()
        })
        .eq('id', id)
        .eq('owner_id', ownerId);

    if (error) throw error;
    return true;
}

/**
 * Restaurar documento soft-deleted.
 */
export async function restoreDocument(id) {
    const ownerId = await getOwnerId();
    if (!id) throw new Error('ID inválido.');

    const { error } = await tenantDb().from('documents')
        .update({
            deleted_at: null,
            deleted_by: null,
            retencao_ate: null,
            updated_at: new Date().toISOString()
        })
        .eq('id', id)
        .eq('owner_id', ownerId);

    if (error) throw error;
    return true;
}

// ── Download URL ────────────────────────────────────────────

/**
 * Gera URL assinada para download (valida por 60s por padrao).
 */
export async function getDownloadUrl(bucketPath, expiresIn = 60, bucket = BUCKET) {
    const { data, error } = await supabase.storage
        .from(bucket)
        .createSignedUrl(bucketPath, expiresIn);

    if (error) throw error;
    return data?.signedUrl;
}

// ── Tags (autocomplete) ────────────────────────────────────

/**
 * Retorna tags unicas ja usadas pelo owner (para autocomplete).
 */
export async function getUsedTags() {
    const ownerId = await getOwnerId();

    const { data, error } = await tenantDb().from('documents')
        .select('tags')
        .eq('owner_id', ownerId)
        .is('deleted_at', null);

    if (error) throw error;

    const set = new Set();
    for (const row of data || []) {
        for (const tag of row.tags || []) {
            if (tag) set.add(tag);
        }
    }
    return [...set].sort((a, b) => a.localeCompare(b, 'pt-BR'));
}

// ── V#51: verificação de integridade ──────────────────────────
/**
 * Baixa o documento e recalcula o SHA-256, compara com o registrado.
 * @returns {{ ok: boolean, expected: string|null, actual: string|null }}
 */
export async function verifyDocumentIntegrity(docId) {
    const { data: doc, error } = await tenantDb().from('documents')
        .select('id, bucket_path, storage_bucket, content_sha256')
        .eq('id', docId)
        .single();
    if (error) throw error;
    if (!doc?.content_sha256) {
        return { ok: false, expected: null, actual: null, reason: 'sem_hash_registrado' };
    }

    const { data: blob, error: dlErr } = await supabase.storage
        .from(doc.storage_bucket || BUCKET)
        .download(doc.bucket_path);
    if (dlErr) throw dlErr;

    const buf = await blob.arrayBuffer();
    const hashBuf = await crypto.subtle.digest('SHA-256', buf);
    const actual = Array.from(new Uint8Array(hashBuf))
        .map((b) => b.toString(16).padStart(2, '0'))
        .join('');

    return {
        ok: actual === doc.content_sha256,
        expected: doc.content_sha256,
        actual
    };
}
