-- ============================================
-- CREAR TABLAS DE DISPONIBILIDAD
-- ============================================

-- ============================================
-- AGREGAR CAMPOS DE HORARIO A STAFF
-- ============================================

DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'staff' AND column_name = 'work_hours_start') THEN
        ALTER TABLE staff ADD COLUMN work_hours_start TIME;
        RAISE NOTICE 'Added work_hours_start to staff';
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'staff' AND column_name = 'work_hours_end') THEN
        ALTER TABLE staff ADD COLUMN work_hours_end TIME;
        RAISE NOTICE 'Added work_hours_end to staff';
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'staff' AND column_name = 'work_days') THEN
        ALTER TABLE staff ADD COLUMN work_days TEXT DEFAULT 'MON,TUE,WED,THU,FRI,SAT';
        RAISE NOTICE 'Added work_days to staff';
    END IF;

    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'staff' AND column_name = 'is_available_for_booking') THEN
        ALTER TABLE staff ADD COLUMN is_available_for_booking BOOLEAN DEFAULT true;
        RAISE NOTICE 'Added is_available_for_booking to staff';
    END IF;
END
$$;

-- ============================================
-- TABLA: booking_blocks
-- Bloqueos de tiempo para recursos específicos
-- ============================================

CREATE TABLE IF NOT EXISTS booking_blocks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    location_id UUID NOT NULL REFERENCES locations(id) ON DELETE CASCADE,
    resource_id UUID NOT NULL REFERENCES resources(id) ON DELETE CASCADE,
    start_time_utc TIMESTAMPTZ NOT NULL,
    end_time_utc TIMESTAMPTZ NOT NULL,
    reason TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID REFERENCES staff(id) ON DELETE SET NULL,
    CONSTRAINT booking_blocks_time_check CHECK (end_time_utc > start_time_utc)
);

CREATE INDEX idx_booking_blocks_location_time ON booking_blocks(location_id, start_time_utc, end_time_utc);
CREATE INDEX idx_booking_blocks_resource ON booking_blocks(resource_id);

-- ============================================
-- TABLA: staff_availability
-- Disponibilidad manual del staff por día
-- ============================================

CREATE TABLE IF NOT EXISTS staff_availability (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    staff_id UUID NOT NULL REFERENCES staff(id) ON DELETE CASCADE,
    date DATE NOT NULL,
    start_time TIME NOT NULL,
    end_time TIME NOT NULL,
    is_available BOOLEAN NOT NULL DEFAULT true,
    reason TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    created_by UUID REFERENCES staff(id) ON DELETE SET NULL,
    CONSTRAINT staff_availability_time_check CHECK (end_time > start_time),
    CONSTRAINT staff_availability_unique UNIQUE (staff_id, date)
);

CREATE INDEX idx_staff_availability_staff_date ON staff_availability(staff_id, date);

-- ============================================
-- FUNCIÓN: check_staff_work_hours
-- Verifica si el staff está en horario laboral
-- ============================================

CREATE OR REPLACE FUNCTION check_staff_work_hours(
    p_staff_id UUID,
    p_start_time_utc TIMESTAMPTZ,
    p_end_time_utc TIMESTAMPTZ,
    p_location_timezone TEXT
)
RETURNS BOOLEAN AS $$
DECLARE
    v_work_hours_start TIME;
    v_work_hours_end TIME;
    v_work_days TEXT;
    v_day_of_week TEXT;
    v_local_start TIME;
    v_local_end TIME;
BEGIN
    -- Obtener horario del staff
    SELECT 
        work_hours_start,
        work_hours_end,
        work_days
    INTO 
        v_work_hours_start,
        v_work_hours_end,
        v_work_days
    FROM staff
    WHERE id = p_staff_id;

    -- Si no tiene horario definido, asumir disponible 24/7
    IF v_work_hours_start IS NULL OR v_work_hours_end IS NULL THEN
        RETURN true;
    END IF;

    -- Obtener día de la semana en zona horaria local
    v_day_of_week := TO_CHAR(p_start_time_utc AT TIME ZONE p_location_timezone, 'DY');

    -- Verificar si trabaja ese día
    IF v_work_days IS NULL OR NOT (',' || v_work_days || ',') LIKE ('%,' || v_day_of_week || ',%') THEN
        RETURN false;
    END IF;

    -- Convertir horas UTC a horario local
    v_local_start := (p_start_time_utc AT TIME ZONE p_location_timezone)::TIME;
    v_local_end := (p_end_time_utc AT TIME ZONE p_location_timezone)::TIME;

    -- Verificar si está dentro del horario laboral
    RETURN v_local_start >= v_work_hours_start AND v_local_end <= v_work_hours_end;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- ============================================
-- FUNCIÓN: check_staff_availability
-- Verifica disponibilidad completa del staff
-- ============================================

CREATE OR REPLACE FUNCTION check_staff_availability(
    p_staff_id UUID,
    p_start_time_utc TIMESTAMPTZ,
    p_end_time_utc TIMESTAMPTZ,
    p_exclude_booking_id UUID DEFAULT NULL
)
RETURNS BOOLEAN AS $$
DECLARE
    v_is_work_hours BOOLEAN;
    v_has_booking_conflict BOOLEAN;
    v_has_manual_block BOOLEAN;
    v_location_timezone TEXT;
BEGIN
    -- Obtener zona horaria de la ubicación del staff
    SELECT timezone INTO v_location_timezone
    FROM locations
    WHERE id = (SELECT location_id FROM staff WHERE id = p_staff_id);

    -- Verificar horario laboral
    v_is_work_hours := check_staff_work_hours(p_staff_id, p_start_time_utc, p_end_time_utc, v_location_timezone);

    IF NOT v_is_work_hours THEN
        RETURN false;
    END IF;

    -- Verificar conflictos con otras reservas
    SELECT EXISTS(
        SELECT 1
        FROM bookings
        WHERE staff_id = p_staff_id
        AND status NOT IN ('cancelled', 'no_show')
        AND (p_exclude_booking_id IS NULL OR id != p_exclude_booking_id)
        AND NOT (p_end_time_utc <= start_time_utc OR p_start_time_utc >= end_time_utc)
    ) INTO v_has_booking_conflict;

    IF v_has_booking_conflict THEN
        RETURN false;
    END IF;

    -- Verificar bloques manuales de disponibilidad
    SELECT EXISTS(
        SELECT 1
        FROM staff_availability
        WHERE staff_id = p_staff_id
        AND date = (p_start_time_utc AT TIME ZONE v_location_timezone)::DATE
        AND is_available = false
        AND NOT (p_end_time_utc AT TIME ZONE v_location_timezone::TIME <= start_time
               OR p_start_time_utc AT TIME ZONE v_location_timezone::TIME >= end_time)
    ) INTO v_has_manual_block;

    IF v_has_manual_block THEN
        RETURN false;
    END IF;

    RETURN true;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- ============================================
-- FUNCIÓN: get_available_staff
-- Obtiene staff disponible para un rango de tiempo
-- ============================================

DROP FUNCTION IF EXISTS get_available_staff(p_location_id UUID, p_start_time_utc TIMESTAMPTZ, p_end_time_utc TIMESTAMPTZ) CASCADE;

CREATE OR REPLACE FUNCTION get_available_staff(
    p_location_id UUID,
    p_start_time_utc TIMESTAMPTZ,
    p_end_time_utc TIMESTAMPTZ
)
RETURNS TABLE (
    staff_id UUID,
    staff_name TEXT,
    role TEXT,
    work_hours_start TIME,
    work_hours_end TIME,
    work_days TEXT,
    location_id UUID
) AS $$
BEGIN
    RETURN QUERY
    SELECT 
        s.id::UUID,
        s.display_name::TEXT,
        s.role::TEXT,
        s.work_hours_start::TIME,
        s.work_hours_end::TIME,
        s.work_days::TEXT,
        s.location_id
    FROM staff s
    WHERE s.location_id = p_location_id
    AND s.is_active = true
    AND s.is_available_for_booking = true
    AND s.role IN ('artist', 'staff', 'manager')
    AND check_staff_availability(s.id, p_start_time_utc, p_end_time_utc)
    ORDER BY
        CASE s.role
            WHEN 'manager' THEN 1
            WHEN 'staff' THEN 2
            WHEN 'artist' THEN 3
        END;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- ============================================
-- FUNCIÓN: get_detailed_availability
-- Obtiene slots de tiempo disponibles
-- ============================================

DROP FUNCTION IF EXISTS get_detailed_availability(p_location_id UUID, p_service_id UUID, p_date DATE, p_time_slot_duration_minutes INTEGER) CASCADE;

CREATE OR REPLACE FUNCTION get_detailed_availability(
    p_location_id UUID,
    p_service_id UUID,
    p_date DATE,
    p_time_slot_duration_minutes INTEGER DEFAULT 60
)
RETURNS JSONB AS $$
DECLARE
    v_service_duration INTEGER;
    v_location_timezone TEXT;
    v_start_time TIME := '09:00'::TIME;
    v_end_time TIME := '21:00'::TIME;
    v_time_slots JSONB := '[]'::JSONB;
    v_slot_start TIMESTAMPTZ;
    v_slot_end TIMESTAMPTZ;
    v_available_staff_count INTEGER;
BEGIN
    -- Obtener duración del servicio
    SELECT duration_minutes INTO v_service_duration
    FROM services
    WHERE id = p_service_id;

    IF v_service_duration IS NULL THEN
        RETURN '[]'::JSONB;
    END IF;

    -- Obtener zona horaria de la ubicación
    SELECT timezone INTO v_location_timezone
    FROM locations
    WHERE id = p_location_id;

    IF v_location_timezone IS NULL THEN
        RETURN '[]'::JSONB;
    END IF;

    -- Generar slots de tiempo para el día
    v_slot_start := (p_date || ' ' || v_start_time::TEXT)::TIMESTAMPTZ
        AT TIME ZONE v_location_timezone;

    v_slot_end := (p_date || ' ' || v_end_time::TEXT)::TIMESTAMPTZ
        AT TIME ZONE v_location_timezone;

    -- Iterar por cada slot
    WHILE v_slot_start < v_slot_end LOOP
        -- Verificar staff disponible para este slot
        SELECT COUNT(*) INTO v_available_staff_count
        FROM (
            SELECT 1
            FROM staff s
            WHERE s.location_id = p_location_id
            AND s.is_active = true
            AND s.is_available_for_booking = true
            AND s.role IN ('artist', 'staff', 'manager')
            AND check_staff_availability(s.id, v_slot_start, v_slot_start + (v_service_duration || ' minutes')::INTERVAL)
        ) AS available_staff;

        -- Agregar slot al resultado
        IF v_available_staff_count > 0 THEN
            v_time_slots := v_time_slots || jsonb_build_object(
                'start_time', v_slot_start::TEXT,
                'end_time', (v_slot_start + (p_time_slot_duration_minutes || ' minutes')::INTERVAL)::TEXT,
                'available', true,
                'available_staff_count', v_available_staff_count
            );
        END IF;

        -- Avanzar al siguiente slot
        v_slot_start := v_slot_start + (p_time_slot_duration_minutes || ' minutes')::INTERVAL;
    END LOOP;

    RETURN v_time_slots;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- ============================================
-- VERIFICACIÓN
-- ============================================

DO $$
BEGIN
    RAISE NOTICE '==========================================';
    RAISE NOTICE 'SISTEMA DE DISPONIBILIDAD COMPLETADO';
    RAISE NOTICE '==========================================';
    RAISE NOTICE 'Tablas creadas:';
    RAISE NOTICE '  - booking_blocks';
    RAISE NOTICE '  - staff_availability';
    RAISE NOTICE 'Funciones RPC creadas:';
    RAISE NOTICE '  - check_staff_work_hours';
    RAISE NOTICE '  - check_staff_availability';
    RAISE NOTICE '  - get_available_staff';
    RAISE NOTICE '  - get_detailed_availability';
    RAISE NOTICE '==========================================';
END
$$;
