# Consolidated SQL файл для миграции базы данных Telegram Tinder Bot
# Этот файл содержит все необходимые SQL-запросы для создания базы данных с нуля

-- Создание расширения для UUID
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Создание перечислений
CREATE TYPE gender_type AS ENUM ('male', 'female', 'other');
CREATE TYPE swipe_action AS ENUM ('like', 'dislike', 'superlike');

-- Создание таблицы пользователей
CREATE TABLE IF NOT EXISTS users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    telegram_id BIGINT UNIQUE NOT NULL,
    username VARCHAR(255),
    first_name VARCHAR(255) NOT NULL,
    last_name VARCHAR(255),
    language_code VARCHAR(10),
    is_premium BOOLEAN DEFAULT FALSE,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Создание таблицы профилей
CREATE TABLE IF NOT EXISTS profiles (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    age INTEGER NOT NULL CHECK (age >= 18),
    gender gender_type NOT NULL,
    bio TEXT,
    photos TEXT[], -- JSON array of photo file_ids
    location VARCHAR(255),
    job VARCHAR(255),
    interests TEXT[], -- JSON array of interests
    last_active TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    is_completed BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_user_profile UNIQUE (user_id)
);

-- Создание индекса для поиска по возрасту и полу
CREATE INDEX idx_profiles_age_gender ON profiles(age, gender);

-- Создание таблицы предпочтений поиска
CREATE TABLE IF NOT EXISTS search_preferences (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    age_min INTEGER NOT NULL DEFAULT 18 CHECK (age_min >= 18),
    age_max INTEGER NOT NULL DEFAULT 99 CHECK (age_max >= age_min),
    looking_for gender_type NOT NULL,
    distance_max INTEGER, -- max distance in km, NULL means no limit
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_user_preferences UNIQUE (user_id)
);

-- Создание таблицы действий (лайки/дизлайки)
CREATE TABLE IF NOT EXISTS swipes (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    target_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    action swipe_action NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_swipe UNIQUE (user_id, target_id)
);

-- Создание индекса для быстрого поиска матчей
CREATE INDEX idx_swipes_user_target ON swipes(user_id, target_id);

-- Создание таблицы матчей
CREATE TABLE IF NOT EXISTS matches (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id_1 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    user_id_2 UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    is_active BOOLEAN DEFAULT TRUE,
    CONSTRAINT unique_match UNIQUE (user_id_1, user_id_2)
);

-- Создание индекса для быстрого поиска матчей по пользователю
CREATE INDEX idx_matches_user_id_1 ON matches(user_id_1);
CREATE INDEX idx_matches_user_id_2 ON matches(user_id_2);

-- Создание таблицы блокировок
CREATE TABLE IF NOT EXISTS blocks (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    blocker_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    blocked_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_block UNIQUE (blocker_id, blocked_id)
);

-- Создание таблицы сообщений
CREATE TABLE IF NOT EXISTS messages (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    match_id UUID NOT NULL REFERENCES matches(id) ON DELETE CASCADE,
    sender_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    text TEXT NOT NULL,
    is_read BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Создание индекса для быстрого поиска сообщений
CREATE INDEX idx_messages_match_id ON messages(match_id);

-- Создание таблицы уведомлений
CREATE TABLE IF NOT EXISTS notifications (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    type VARCHAR(50) NOT NULL, -- new_match, new_message, etc.
    content TEXT NOT NULL,
    is_read BOOLEAN DEFAULT FALSE,
    reference_id UUID, -- Can reference a match_id, message_id, etc.
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Создание индекса для быстрого поиска уведомлений
CREATE INDEX idx_notifications_user_id ON notifications(user_id);

-- Создание таблицы настроек
CREATE TABLE IF NOT EXISTS settings (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    notifications_enabled BOOLEAN DEFAULT TRUE,
    show_online_status BOOLEAN DEFAULT TRUE,
    visibility BOOLEAN DEFAULT TRUE, -- whether profile is visible in search
    theme VARCHAR(20) DEFAULT 'light',
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_user_settings UNIQUE (user_id)
);

-- Создание таблицы просмотров профиля
CREATE TABLE IF NOT EXISTS profile_views (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    viewer_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    viewed_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    view_count INTEGER DEFAULT 1,
    last_viewed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_view UNIQUE (viewer_id, viewed_id)
);

-- Создание таблицы для премиум-пользователей
CREATE TABLE IF NOT EXISTS premium_features (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    is_premium BOOLEAN DEFAULT FALSE,
    superlike_quota INTEGER DEFAULT 1,
    spotlight_quota INTEGER DEFAULT 0,
    see_likes BOOLEAN DEFAULT FALSE, -- Can see who liked their profile
    unlimited_likes BOOLEAN DEFAULT FALSE,
    expires_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_user_premium UNIQUE (user_id)
);

-- Функция для обновления поля updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Триггеры для обновления поля updated_at
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER update_profiles_updated_at
BEFORE UPDATE ON profiles
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER update_search_preferences_updated_at
BEFORE UPDATE ON search_preferences
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER update_settings_updated_at
BEFORE UPDATE ON settings
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER update_premium_features_updated_at
BEFORE UPDATE ON premium_features
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

-- Индекс для поиска пользователей по Telegram ID (часто используемый запрос)
CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);

-- Индекс для статуса профиля (активный/неактивный, завершенный/незавершенный)
CREATE INDEX IF NOT EXISTS idx_profiles_is_completed ON profiles(is_completed);

-- Представление для статистики
CREATE OR REPLACE VIEW user_statistics AS
SELECT 
    u.id,
    u.telegram_id,
    (SELECT COUNT(*) FROM swipes WHERE user_id = u.id AND action = 'like') AS likes_given,
    (SELECT COUNT(*) FROM swipes WHERE user_id = u.id AND action = 'dislike') AS dislikes_given,
    (SELECT COUNT(*) FROM swipes WHERE target_id = u.id AND action = 'like') AS likes_received,
    (SELECT COUNT(*) FROM matches WHERE user_id_1 = u.id OR user_id_2 = u.id) AS matches_count,
    (SELECT COUNT(*) FROM messages WHERE sender_id = u.id) AS messages_sent,
    (SELECT COUNT(*) FROM profile_views WHERE viewed_id = u.id) AS profile_views
FROM users u;

-- Функция для создания матча при взаимных лайках
CREATE OR REPLACE FUNCTION create_match_on_mutual_like()
RETURNS TRIGGER AS $$
DECLARE
    reverse_like_exists BOOLEAN;
BEGIN
    -- Check if there is a reverse like
    SELECT EXISTS (
        SELECT 1 
        FROM swipes 
        WHERE user_id = NEW.target_id 
        AND target_id = NEW.user_id 
        AND action = 'like'
    ) INTO reverse_like_exists;
    
    -- If there is a reverse like, create a match
    IF reverse_like_exists AND NEW.action = 'like' THEN
        INSERT INTO matches (user_id_1, user_id_2)
        VALUES (
            LEAST(NEW.user_id, NEW.target_id),
            GREATEST(NEW.user_id, NEW.target_id)
        )
        ON CONFLICT (user_id_1, user_id_2) DO NOTHING;
        
        -- Create notifications for both users
        INSERT INTO notifications (user_id, type, content, reference_id)
        VALUES (
            NEW.user_id,
            'new_match',
            'У вас новый матч!',
            (SELECT id FROM matches WHERE 
                (user_id_1 = LEAST(NEW.user_id, NEW.target_id) AND user_id_2 = GREATEST(NEW.user_id, NEW.target_id))
            )
        );
        
        INSERT INTO notifications (user_id, type, content, reference_id)
        VALUES (
            NEW.target_id,
            'new_match',
            'У вас новый матч!',
            (SELECT id FROM matches WHERE 
                (user_id_1 = LEAST(NEW.user_id, NEW.target_id) AND user_id_2 = GREATEST(NEW.user_id, NEW.target_id))
            )
        );
    END IF;
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Триггер для создания матча при взаимных лайках
CREATE TRIGGER create_match_trigger
AFTER INSERT ON swipes
FOR EACH ROW
EXECUTE FUNCTION create_match_on_mutual_like();

-- Функция для создания уведомления о новом сообщении
CREATE OR REPLACE FUNCTION notify_new_message()
RETURNS TRIGGER AS $$
DECLARE
    recipient_id UUID;
    match_record RECORD;
BEGIN
    -- Get the match record
    SELECT * INTO match_record FROM matches WHERE id = NEW.match_id;
    
    -- Determine the recipient
    IF match_record.user_id_1 = NEW.sender_id THEN
        recipient_id := match_record.user_id_2;
    ELSE
        recipient_id := match_record.user_id_1;
    END IF;
    
    -- Create notification
    INSERT INTO notifications (user_id, type, content, reference_id)
    VALUES (
        recipient_id,
        'new_message',
        'У вас новое сообщение!',
        NEW.id
    );
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Триггер для создания уведомления о новом сообщении
CREATE TRIGGER notify_new_message_trigger
AFTER INSERT ON messages
FOR EACH ROW
EXECUTE FUNCTION notify_new_message();

-- Функция для обновления времени последней активности пользователя
CREATE OR REPLACE FUNCTION update_last_active()
RETURNS TRIGGER AS $$
BEGIN
    UPDATE profiles
    SET last_active = CURRENT_TIMESTAMP
    WHERE user_id = NEW.user_id;
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Триггер для обновления времени последней активности при свайпах
CREATE TRIGGER update_last_active_on_swipe
AFTER INSERT ON swipes
FOR EACH ROW
EXECUTE FUNCTION update_last_active();

-- Триггер для обновления времени последней активности при отправке сообщений
CREATE TRIGGER update_last_active_on_message
AFTER INSERT ON messages
FOR EACH ROW
EXECUTE FUNCTION update_last_active();

-- Создание функции для автоматического создания профиля при создании пользователя
CREATE OR REPLACE FUNCTION create_initial_profile()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO profiles (user_id, name, age, gender)
    VALUES (NEW.id, COALESCE(NEW.first_name, 'User'), 18, 'other');
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Триггер для автоматического создания профиля при создании пользователя
CREATE TRIGGER create_profile_trigger
AFTER INSERT ON users
FOR EACH ROW
EXECUTE FUNCTION create_initial_profile();

-- Индексы для оптимизации частых запросов
CREATE INDEX IF NOT EXISTS idx_profiles_last_active ON profiles(last_active);
CREATE INDEX IF NOT EXISTS idx_swipes_action ON swipes(action);
CREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications(is_read);
CREATE INDEX IF NOT EXISTS idx_messages_is_read ON messages(is_read);

-- Добавление ограничений для проверки возраста
ALTER TABLE profiles DROP CONSTRAINT IF EXISTS age_check;
ALTER TABLE profiles ADD CONSTRAINT age_check CHECK (age >= 18 AND age <= 99);

-- Добавление ограничений для предпочтений
ALTER TABLE search_preferences DROP CONSTRAINT IF EXISTS age_range_check;
ALTER TABLE search_preferences ADD CONSTRAINT age_range_check CHECK (age_min >= 18 AND age_max >= age_min AND age_max <= 99);

-- Комментарии к таблицам для документации
COMMENT ON TABLE users IS 'Таблица пользователей Telegram';
COMMENT ON TABLE profiles IS 'Профили пользователей для знакомств';
COMMENT ON TABLE search_preferences IS 'Предпочтения поиска пользователей';
COMMENT ON TABLE swipes IS 'История лайков/дислайков';
COMMENT ON TABLE matches IS 'Совпадения (матчи) между пользователями';
COMMENT ON TABLE messages IS 'Сообщения между пользователями';
COMMENT ON TABLE notifications IS 'Уведомления для пользователей';
COMMENT ON TABLE settings IS 'Настройки пользователей';
COMMENT ON TABLE profile_views IS 'История просмотров профилей';
COMMENT ON TABLE premium_features IS 'Премиум-функции для пользователей';

-- Представление для быстрого получения активных матчей с информацией о пользователе
CREATE OR REPLACE VIEW active_matches AS
SELECT 
    m.id AS match_id,
    m.created_at AS match_date,
    CASE 
        WHEN m.user_id_1 = u1.id THEN u2.id 
        ELSE u1.id 
    END AS partner_id,
    CASE 
        WHEN m.user_id_1 = u1.id THEN u2.telegram_id 
        ELSE u1.telegram_id 
    END AS partner_telegram_id,
    CASE 
        WHEN m.user_id_1 = u1.id THEN p2.name 
        ELSE p1.name 
    END AS partner_name,
    CASE 
        WHEN m.user_id_1 = u1.id THEN p2.photos[1] 
        ELSE p1.photos[1] 
    END AS partner_photo,
    (SELECT COUNT(*) FROM messages WHERE match_id = m.id) AS message_count,
    (SELECT COUNT(*) FROM messages WHERE match_id = m.id AND is_read = false AND sender_id != u1.id) AS unread_count,
    m.is_active
FROM matches m
JOIN users u1 ON m.user_id_1 = u1.id
JOIN users u2 ON m.user_id_2 = u2.id
JOIN profiles p1 ON u1.id = p1.user_id
JOIN profiles p2 ON u2.id = p2.user_id
WHERE m.is_active = true;
