CREATE OR REPLACE FUNCTION public.notify_users()
RETURNS TRIGGER AS $$
DECLARE
    affected_users UUID[];
    affected_user UUID;
    actor UUID;
    action TEXT;
    msg TEXT;
    meta JSONB;
    entity_type_name TEXT;
    entity_identifier TEXT;
BEGIN
    -- Determine if this is an INSERT or UPDATE
    IF TG_OP = 'INSERT' THEN
        action := 'created';
    ELSE
        action := 'updated';
        -- Skip if soft delete happened (only for tables with deleted_at column)
        IF TG_TABLE_NAME IN ('tablos', 'events', 'notes', 'event_types') THEN
            IF NEW.deleted_at IS NOT NULL AND OLD.deleted_at IS NULL THEN
                RETURN NEW;
            END IF;
        END IF;
    END IF;

    -- Get the actor (current user)
    -- Note: auth.uid() may return NULL when called via service role (API calls)
    -- In such cases, we won't exclude anyone from notifications based on actor
    actor := auth.uid();
    
    -- Set entity type and ID based on the table
    entity_type_name := TG_TABLE_NAME;
    
    -- Determine entity ID and affected users based on table
    CASE TG_TABLE_NAME
        WHEN 'tablos' THEN
            entity_identifier := NEW.id;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'New tablo "' || NEW.name || '" was created';
                -- Notify owner (but not if they are the creator)
                IF actor IS NULL OR NEW.owner_id != actor THEN
                    affected_users := ARRAY[NEW.owner_id];
                END IF;
            ELSE
                msg := 'Tablo "' || NEW.name || '" was updated';
                -- Notify owner and all collaborators (exclude actor if set)
                affected_users := ARRAY(
                    SELECT DISTINCT user_id 
                    FROM public.tablo_access 
                    WHERE tablo_id = NEW.id 
                        AND is_active = true
                        AND (actor IS NULL OR user_id != actor)
                    UNION
                    SELECT NEW.owner_id
                    WHERE actor IS NULL OR NEW.owner_id != actor
                );
            END IF;
            
            meta := jsonb_build_object(
                'tablo_name', NEW.name,
                'status', NEW.status,
                'color', NEW.color
            );

        WHEN 'tasks' THEN
            entity_identifier := NEW.id;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'New task "' || NEW.title || '" was created';
            ELSE
                IF OLD.status != NEW.status THEN
                    msg := 'Task "' || NEW.title || '" status changed to ' || NEW.status;
                ELSIF OLD.assignee_id != NEW.assignee_id OR (OLD.assignee_id IS NULL AND NEW.assignee_id IS NOT NULL) THEN
                    msg := 'You were assigned to task "' || NEW.title || '"';
                ELSE
                    msg := 'Task "' || NEW.title || '" was updated';
                END IF;
            END IF;
            
            -- Notify tablo collaborators and assignee (exclude actor if set)
            affected_users := ARRAY(
                SELECT DISTINCT user_id 
                FROM public.tablo_access 
                WHERE tablo_id = NEW.tablo_id 
                    AND is_active = true
                    AND (actor IS NULL OR user_id != actor)
                UNION
                SELECT t.owner_id
                FROM public.tablos t
                WHERE t.id = NEW.tablo_id
                    AND (actor IS NULL OR t.owner_id != actor)
                UNION
                SELECT NEW.assignee_id
                WHERE NEW.assignee_id IS NOT NULL
                    AND (actor IS NULL OR NEW.assignee_id != actor)
            );
            
            meta := jsonb_build_object(
                'task_title', NEW.title,
                'status', NEW.status,
                'tablo_id', NEW.tablo_id,
                'assignee_id', NEW.assignee_id
            );

        WHEN 'events' THEN
            entity_identifier := NEW.id;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'New event "' || NEW.title || '" was created';
            ELSE
                msg := 'Event "' || NEW.title || '" was updated';
            END IF;
            
            -- Notify tablo collaborators (exclude actor if set)
            affected_users := ARRAY(
                SELECT DISTINCT user_id 
                FROM public.tablo_access 
                WHERE tablo_id = NEW.tablo_id 
                    AND is_active = true
                    AND (actor IS NULL OR user_id != actor)
                UNION
                SELECT t.owner_id
                FROM public.tablos t
                WHERE t.id = NEW.tablo_id
                    AND (actor IS NULL OR t.owner_id != actor)
                UNION
                SELECT NEW.created_by
                WHERE actor IS NULL OR NEW.created_by != actor
            );
            
            meta := jsonb_build_object(
                'event_title', NEW.title,
                'start_date', NEW.start_date,
                'start_time', NEW.start_time,
                'tablo_id', NEW.tablo_id
            );

        WHEN 'notes' THEN
            entity_identifier := NEW.id;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'New note "' || NEW.title || '" was created';
            ELSE
                msg := 'Note "' || NEW.title || '" was updated';
            END IF;
            
            -- Notify note owner and users with access (exclude actor if set)
            affected_users := ARRAY(
                SELECT DISTINCT user_id 
                FROM public.note_access 
                WHERE note_id = NEW.id 
                    AND is_active = true
                    AND (actor IS NULL OR user_id != actor)
                UNION
                SELECT NEW.user_id
                WHERE actor IS NULL OR NEW.user_id != actor
            );
            
            meta := jsonb_build_object(
                'note_title', NEW.title,
                'note_owner', NEW.user_id
            );

        WHEN 'tablo_access' THEN
            entity_identifier := NEW.id::TEXT;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'You were granted access to a tablo';
            ELSE
                IF OLD.is_admin != NEW.is_admin AND NEW.is_admin = true THEN
                    msg := 'You were promoted to admin on a tablo';
                ELSIF OLD.is_active != NEW.is_active AND NEW.is_active = false THEN
                    msg := 'Your access to a tablo was revoked';
                ELSE
                    msg := 'Your tablo access was updated';
                END IF;
            END IF;
            
            -- Notify the user being granted/modified access (exclude actor if set)
            IF actor IS NULL OR NEW.user_id != actor THEN
                affected_users := ARRAY[NEW.user_id];
            END IF;
            
            meta := jsonb_build_object(
                'tablo_id', NEW.tablo_id,
                'is_admin', NEW.is_admin,
                'is_active', NEW.is_active,
                'granted_by', NEW.granted_by
            );

        WHEN 'tablo_invites' THEN
            entity_identifier := NEW.id::TEXT;
            
            -- For tablo_invites, use invited_by as actor instead of auth.uid()
            -- because this is called via service role which has no auth context
            actor := NEW.invited_by;
            
            RAISE LOG 'notify_users: New tablo invite: %', NEW;
            RAISE LOG 'notify_users: Processing tablo_invites, TG_OP=%, email=%, actor=%', TG_OP, NEW.invited_email, actor;
            
            IF TG_OP = 'INSERT' THEN
                msg := 'You were invited to collaborate on a tablo';
            ELSE
                IF OLD.is_pending != NEW.is_pending AND NEW.is_pending = false THEN
                    msg := 'Your tablo invitation status changed';
                ELSE
                    msg := 'Your tablo invitation was updated';
                END IF;
            END IF;
            
            RAISE LOG 'notify_users: Looking for user with email=%, actor=%', NEW.invited_email, actor;
            
            -- Find the user by email and notify them (case-insensitive)
            -- Only notify if user exists in the system
            -- Don't notify the person who sent the invite (exclude actor if set)
            affected_users := ARRAY(
                SELECT id 
                FROM auth.users 
                WHERE LOWER(email) = LOWER(NEW.invited_email)
                    AND (actor IS NULL OR id != actor)
            );
            
            RAISE LOG 'notify_users: Found % affected users for invite', COALESCE(array_length(affected_users, 1), 0);
            
            meta := jsonb_build_object(
                'tablo_id', NEW.tablo_id,
                'invited_email', NEW.invited_email,
                'invited_by', NEW.invited_by,
                'is_pending', NEW.is_pending
            );

        ELSE
            -- Unknown table, skip
            RETURN NEW;
    END CASE;

    -- Insert notifications for all affected users
    RAISE LOG 'notify_users: About to insert notifications for % users, entity_type=%', COALESCE(array_length(affected_users, 1), 0), entity_type_name;
    
    IF affected_users IS NOT NULL AND array_length(affected_users, 1) > 0 THEN
        FOREACH affected_user IN ARRAY affected_users
        LOOP
            RAISE LOG 'notify_users: Inserting notification for user_id=%, entity_type=%, message=%', affected_user, entity_type_name, msg;
            
            INSERT INTO public.notifications (
                user_id,
                actor_id,
                entity_type,
                entity_id,
                action_type,
                message,
                metadata
            ) VALUES (
                affected_user,
                actor,
                entity_type_name,
                entity_identifier,
                action,
                msg,
                meta
            );
        END LOOP;
    ELSE
        RAISE LOG 'notify_users: No affected users to notify';
    END IF;

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