/**
 * agendaMappers.spec.js
 *
 * Testa as funções de mapeamento de dados da agenda:
 *   - mapAgendaEventosToCalendarEvents
 *   - mapAgendaEventosToClinicResourceEvents
 *   - buildNextSessions
 *   - minutesToDuration
 *   - tituloFallback
 *   - calcDefaultSlotDuration
 *   - buildWeeklyBreakBackgroundEvents
 */

import { describe, it, expect } from 'vitest';
import { mapAgendaEventosToCalendarEvents, mapAgendaEventosToClinicResourceEvents, buildNextSessions, minutesToDuration, tituloFallback, calcDefaultSlotDuration, buildWeeklyBreakBackgroundEvents } from '../agendaMappers.js';

// ─── fixtures ─────────────────────────────────────────────────────────────────

function evento(overrides = {}) {
    return {
        id: 'ev-1',
        titulo: 'Sessão Teste',
        tipo: 'sessao',
        status: 'agendado',
        inicio_em: '2026-03-10T09:00:00',
        fim_em: '2026-03-10T10:00:00',
        owner_id: 'owner-1',
        tenant_id: 'tenant-1',
        patient_id: 'patient-1',
        modalidade: 'presencial',
        ...overrides
    };
}

// ─── mapAgendaEventosToCalendarEvents ─────────────────────────────────────────

describe('mapAgendaEventosToCalendarEvents', () => {
    it('mapeia um evento simples para o shape do FullCalendar', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento()]);
        expect(ev.id).toBe('ev-1');
        expect(ev.start).toBe('2026-03-10T09:00:00');
        expect(ev.end).toBe('2026-03-10T10:00:00');
        expect(ev.extendedProps.tipo).toBe('sessao');
        expect(ev.extendedProps.status).toBe('agendado');
    });

    it('filtra rows null/undefined', () => {
        const result = mapAgendaEventosToCalendarEvents([null, undefined, evento()]);
        expect(result.length).toBe(1);
    });

    it('retorna array vazio para input vazio', () => {
        expect(mapAgendaEventosToCalendarEvents([])).toEqual([]);
        expect(mapAgendaEventosToCalendarEvents(null)).toEqual([]);
    });

    it('inclui ícone ✓ no título para status realizado', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'realizado' })]);
        expect(ev.title).toContain('✓');
    });

    it('inclui ícone ✗ no título para status faltou', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'faltou' })]);
        expect(ev.title).toContain('✗');
    });

    it('inclui ícone ∅ no título para status cancelado', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'cancelado' })]);
        expect(ev.title).toContain('∅');
    });

    it('inclui ícone ↺ no título para status remarcado', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'remarcado' })]);
        expect(ev.title).toContain('↺');
    });

    it('inclui ícone ↻ para ocorrências de série', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ recurrence_id: 'rule-1', is_occurrence: true })]);
        expect(ev.title).toContain('↻');
    });

    it('aplica cor de fundo para status faltou', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'faltou' })]);
        expect(ev.backgroundColor).toBe('#f97316');
    });

    it('aplica cor de fundo para status cancelado', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'cancelado' })]);
        expect(ev.backgroundColor).toBe('#ef4444');
    });

    it('aplica cor de fundo para status remarcado', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ status: 'remarcado' })]);
        expect(ev.backgroundColor).toBe('#a855f7');
    });

    it('usa titulo_custom quando disponível', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ titulo_custom: 'Personalizado' })]);
        expect(ev.title).toContain('Personalizado');
    });

    it('usa nome do paciente via patients join quando titulo ausente', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([
            evento({
                titulo: null,
                titulo_custom: null,
                patients: { nome_completo: 'João Silva', avatar_url: null }
            })
        ]);
        expect(ev.title).toContain('João Silva');
    });

    it('mapeia patient_id corretamente', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([evento({ patient_id: 'p-123' })]);
        expect(ev.extendedProps.patient_id).toBe('p-123');
        expect(ev.extendedProps.paciente_id).toBe('p-123'); // alias
    });

    it('mapeia recurrence_id e original_date', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([
            evento({
                recurrence_id: 'rule-abc',
                original_date: '2026-03-10'
            })
        ]);
        expect(ev.extendedProps.recurrence_id).toBe('rule-abc');
        expect(ev.extendedProps.original_date).toBe('2026-03-10');
    });

    it('mapeia exception_type', () => {
        const [ev] = mapAgendaEventosToCalendarEvents([
            evento({
                exception_type: 'patient_missed',
                status: 'faltou'
            })
        ]);
        expect(ev.extendedProps.exception_type).toBe('patient_missed');
        expect(ev.extendedProps.is_exception).toBe(true);
    });
});

// ─── mapAgendaEventosToClinicResourceEvents ───────────────────────────────────

describe('mapAgendaEventosToClinicResourceEvents', () => {
    it('adiciona resourceId baseado em owner_id', () => {
        const [ev] = mapAgendaEventosToClinicResourceEvents([evento({ owner_id: 'owner-99' })]);
        expect(ev.resourceId).toBe('owner-99');
    });

    it('usa terapeuta_id como fallback para resourceId', () => {
        const [ev] = mapAgendaEventosToClinicResourceEvents([evento({ owner_id: null, terapeuta_id: 'tera-1' })]);
        expect(ev.resourceId).toBe('tera-1');
    });
});

// ─── buildNextSessions ────────────────────────────────────────────────────────

describe('buildNextSessions', () => {
    it('filtra sessões no passado', () => {
        const now = new Date('2026-03-10T12:00:00');
        const rows = [evento({ id: 'past', fim_em: '2026-03-09T10:00:00' }), evento({ id: 'future', fim_em: '2026-03-11T10:00:00' })];
        const result = buildNextSessions(rows, now);
        expect(result.length).toBe(1);
        expect(result[0].id).toBe('future');
    });

    it('inclui sessão cujo fim_em é agora (mesmo ms)', () => {
        const now = new Date('2026-03-10T10:00:00');
        const rows = [evento({ fim_em: '2026-03-10T10:00:00' })];
        const result = buildNextSessions(rows, now);
        expect(result.length).toBe(1);
    });

    it('limita a 6 sessões', () => {
        const now = new Date('2026-01-01');
        const rows = Array.from({ length: 10 }, (_, i) =>
            evento({
                id: `ev-${i}`,
                fim_em: `2026-03-${String(i + 10).padStart(2, '0')}T10:00:00`
            })
        );
        const result = buildNextSessions(rows, now);
        expect(result.length).toBe(6);
    });

    it('retorna shape correto', () => {
        const now = new Date('2026-01-01');
        const [s] = buildNextSessions([evento()], now);
        expect(s).toMatchObject({
            id: 'ev-1',
            title: 'Sessão Teste',
            startISO: '2026-03-10T09:00:00',
            endISO: '2026-03-10T10:00:00',
            tipo: 'sessao',
            status: 'agendado'
        });
    });

    it('mapeia pacienteId de patient_id', () => {
        const now = new Date('2026-01-01');
        const [s] = buildNextSessions([evento({ patient_id: 'p-999' })], now);
        expect(s.pacienteId).toBe('p-999');
    });
});

// ─── minutesToDuration ────────────────────────────────────────────────────────

describe('minutesToDuration', () => {
    it('30 minutos → 00:30:00', () => expect(minutesToDuration(30)).toBe('00:30:00'));
    it('60 minutos → 01:00:00', () => expect(minutesToDuration(60)).toBe('01:00:00'));
    it('90 minutos → 01:30:00', () => expect(minutesToDuration(90)).toBe('01:30:00'));
    it('0 minutos  → 00:00:00', () => expect(minutesToDuration(0)).toBe('00:00:00'));
});

// ─── tituloFallback ───────────────────────────────────────────────────────────

describe('tituloFallback', () => {
    it('sessao → Sessão', () => expect(tituloFallback('sessao')).toBe('Sessão'));
    it('bloqueio → Bloqueio', () => expect(tituloFallback('bloqueio')).toBe('Bloqueio'));
    it('pessoal → Pessoal', () => expect(tituloFallback('pessoal')).toBe('Pessoal'));
    it('clinica → Clínica', () => expect(tituloFallback('clinica')).toBe('Clínica'));
    it('desconhecido → Compromisso', () => expect(tituloFallback('outro')).toBe('Compromisso'));
    it('null → Compromisso', () => expect(tituloFallback(null)).toBe('Compromisso'));
});

// ─── calcDefaultSlotDuration ──────────────────────────────────────────────────

describe('calcDefaultSlotDuration', () => {
    it('usa granularidade custom quando ativa', () => {
        const s = { usar_granularidade_custom: true, granularidade_min: 15 };
        expect(calcDefaultSlotDuration(s)).toBe('00:15:00');
    });

    it('usa admin_slot_visual_minutos como fallback', () => {
        const s = { admin_slot_visual_minutos: 20 };
        expect(calcDefaultSlotDuration(s)).toBe('00:20:00');
    });

    it('usa 30 min como padrão quando nenhuma configuração', () => {
        expect(calcDefaultSlotDuration({})).toBe('00:30:00');
        expect(calcDefaultSlotDuration(null)).toBe('00:30:00');
    });
});

// ─── buildWeeklyBreakBackgroundEvents ────────────────────────────────────────

describe('buildWeeklyBreakBackgroundEvents', () => {
    it('retorna vazio para input vazio', () => {
        const result = buildWeeklyBreakBackgroundEvents([], new Date('2026-03-01'), new Date('2026-03-08'));
        expect(result).toEqual([]);
    });

    it('gera eventos de background para pausa no dia correto', () => {
        const pausas = [{ weekday: 1, start: '12:00', end: '13:00', label: 'Almoço' }]; // segunda
        const result = buildWeeklyBreakBackgroundEvents(
            pausas,
            new Date(2026, 2, 1), // dom
            new Date(2026, 2, 8) // dom
        );
        expect(result.length).toBe(1);
        expect(result[0].display).toBe('background');
        expect(result[0].start).toContain('2026-03-02'); // segunda
        expect(result[0].extendedProps.label).toBe('Almoço');
    });

    it('gera uma pausa por semana quando range cobre 2 semanas', () => {
        const pausas = [{ weekday: 1, start: '12:00', end: '13:00' }]; // toda segunda
        const result = buildWeeklyBreakBackgroundEvents(
            pausas,
            new Date(2026, 2, 1), // dom 01/03
            new Date(2026, 2, 15) // dom 15/03
        );
        expect(result.length).toBe(2); // seg 02 e seg 09
    });

    it('não gera para dias diferentes', () => {
        const pausas = [{ weekday: 5, start: '12:00', end: '13:00' }]; // sexta
        const result = buildWeeklyBreakBackgroundEvents(
            pausas,
            new Date(2026, 2, 2), // seg
            new Date(2026, 2, 5) // qui
        );
        expect(result.length).toBe(0);
    });
});
