const bcrypt = require('bcryptjs');
const fetch = require('node-fetch');

class AuthService {
    constructor() {
        this.db = null;
        this.initialized = false;
    }

    setDatabase(database) {
        this.db = database;
        this.initialized = true;
        console.log('✅ База данных установлена в AuthService');
        this.initUsers();
    }

    async initUsers() {
        if (!this.db) {
            console.log('⚠️ База данных не установлена, откладываем создание пользователей');
            return;
        }

        try {
            // Создаем пользователей из .env
            const users = [
                {
                    login: process.env.USER_1_LOGIN,
                    password: process.env.USER_1_PASSWORD,
                    name: process.env.USER_1_NAME,
                    email: process.env.USER_1_EMAIL,
                    auth_type: 'local'
                },
                {
                    login: process.env.USER_2_LOGIN,
                    password: process.env.USER_2_PASSWORD,
                    name: process.env.USER_2_NAME,
                    email: process.env.USER_2_EMAIL,
                    auth_type: 'local'
                },
                {
                    login: process.env.USER_3_LOGIN,
                    password: process.env.USER_3_PASSWORD,
                    name: process.env.USER_3_NAME,
                    email: process.env.USER_3_EMAIL,
                    auth_type: 'local'
                }
            ];

            for (const userData of users) {
                if (userData.login && userData.password) {
                    await this.createUserIfNotExists(userData);
                }
            }
        } catch (error) {
            console.error('❌ Ошибка инициализации пользователей:', error.message);
        }
    }

    async createUserIfNotExists(userData) {
        return new Promise((resolve, reject) => {
            if (!this.db) {
                console.error('❌ База данных не доступна в createUserIfNotExists');
                reject(new Error('База данных не инициализирована'));
                return;
            }

            this.db.get("SELECT id FROM users WHERE login = ?", [userData.login], async (err, row) => {
                if (err) {
                    reject(err);
                    return;
                }

                if (!row) {
                    const hashedPassword = await bcrypt.hash(userData.password, 10);
                    this.db.run(
                        "INSERT INTO users (login, password, name, email, role, auth_type, created_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))",
                        [
                            userData.login, 
                            hashedPassword, 
                            userData.name, 
                            userData.email, 
                            'teacher', 
                            userData.auth_type || 'local'
                        ],
                        function(err) {
                            if (err) {
                                reject(err);
                            } else {
                                console.log(`✅ Создан пользователь: ${userData.name}`);
                                resolve(this.lastID);
                            }
                        }
                    );
                } else {
                    resolve(row.id);
                }
            });
        });
    }

    async authenticateLocal(login, password) {
        if (!this.db) {
            throw new Error('База данных не инициализирована в AuthService');
        }

        return new Promise((resolve, reject) => {
            this.db.get("SELECT * FROM users WHERE login = ? AND auth_type = 'local'", [login], async (err, user) => {
                if (err) {
                    reject(err);
                    return;
                }

                if (!user) {
                    resolve(null);
                    return;
                }

                try {
                    const isValid = await bcrypt.compare(password, user.password);
                    if (isValid) {
                        // Обновляем last_login
                        this.db.run("UPDATE users SET last_login = datetime('now') WHERE id = ?", [user.id]);
                        
                        // Не возвращаем пароль
                        const { password, ...userWithoutPassword } = user;
                        resolve(userWithoutPassword);
                    } else {
                        resolve(null);
                    }
                } catch (error) {
                    reject(error);
                }
            });
        });
    }

    async authenticateLDAP(username, password) {
        if (!this.db) {
            throw new Error('База данных не инициализирована в AuthService');
        }

        try {
            // Проверяем наличие URL для LDAP
            if (!process.env.LDAP_AUTH_URL) {
                console.log('⚠️ LDAP_AUTH_URL не задан в .env');
                return null;
            }

            const response = await fetch(process.env.LDAP_AUTH_URL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ username, password })
            });

            if (!response.ok) {
                console.log(`⚠️ LDAP сервер вернул ошибку: ${response.status}`);
                return null;
            }

            const data = await response.json();
            
            if (data.success) {
                return this.processLDAPUser(data);
            } else {
                return null;
            }
        } catch (error) {
            console.error('❌ Ошибка LDAP аутентификации:', error.message);
            return null;
        }
    }

    async processLDAPUser(ldapData) {
        if (!this.db) {
            throw new Error('База данных не инициализирована в AuthService');
        }

        const { username, full_name, groups, description } = ldapData;
        
        // Определяем роль пользователя на основе групп
        // Получаем все группы из .env
        const allowedGroups = process.env.ALLOWED_GROUPS ? 
            process.env.ALLOWED_GROUPS.split(',').map(g => g.trim()) : [];

        const secretaryGroups = process.env.SECRETARY_GROUPS ? 
            process.env.SECRETARY_GROUPS.split(',').map(g => g.trim()) : [];

        const helpGroups = process.env.HELP_GROUPS ? 
            process.env.HELP_GROUPS.split(',').map(g => g.trim()) : [];

        const itHelpGroups = process.env.ITHELP_GROUPS ? 
            process.env.ITHELP_GROUPS.split(',').map(g => g.trim()) : [];

        const requestGroups = process.env.REQUEST_GROUPS ? 
            process.env.REQUEST_GROUPS.split(',').map(g => g.trim()) : [];

        const tasksGroups = process.env.TASKS_GROUPS ? 
            process.env.TASKS_GROUPS.split(',').map(g => g.trim()) : [];

        // Все LDAP пользователи являются "teacher" по умолчанию
        let userGroups = groups ? [...groups] : [];
        
        // Проверяем все группы пользователя и определяем роль
        // Если пользователь состоит в нескольких группах, приоритет определяется порядком проверки
        let role = 'teacher'; // Роль по умолчанию для всех пользователей
        
        if (userGroups.length > 0) {
            // Определяем наивысшую роль пользователя
            // Порядок приоритета: admin > secretary > help > ithelp > request > tasks > teacher
            if (userGroups.some(group => allowedGroups.includes(group))) {
                role = 'admin';
            } else if (userGroups.some(group => secretaryGroups.includes(group))) {
                role = 'secretary';
            } else if (userGroups.some(group => helpGroups.includes(group))) {
                role = 'help';
            } else if (userGroups.some(group => itHelpGroups.includes(group))) {
                role = 'ithelp';
            } else if (userGroups.some(group => requestGroups.includes(group))) {
                role = 'request';
            } else if (userGroups.some(group => tasksGroups.includes(group))) {
                role = 'tasks';
            }
            // Если ни одна из специальных групп не найдена, остается 'teacher'
        }
        
        // Сохраняем/обновляем пользователя в базе
        return new Promise((resolve, reject) => {
            this.db.get("SELECT * FROM users WHERE login = ? AND auth_type = 'ldap'", [username], async (err, existingUser) => {
                if (err) {
                    reject(err);
                    return;
                }

                const userData = {
                    login: username,
                    name: full_name || username,
                    email: `${username}@school25.ru`,
                    role: role,
                    auth_type: 'ldap',
                    groups: JSON.stringify(userGroups),
                    description: description || '',
                    last_login: new Date().toISOString()
                };

                if (existingUser) {
                    // Всегда обновляем роль и группы
                    this.db.run(
                        `UPDATE users SET 
                         name = ?, email = ?, role = ?, groups = ?, description = ?, last_login = datetime('now'),
                         updated_at = datetime('now')
                         WHERE id = ?`,
                        [userData.name, userData.email, userData.role, userData.groups, userData.description, existingUser.id],
                        function(err) {
                            if (err) {
                                reject(err);
                            } else {
                                console.log(`✅ Обновлены данные LDAP пользователя ${username}. Роль: ${userData.role}, Группы: ${userGroups.length}`);
                                resolve({ 
                                    id: existingUser.id,
                                    login: userData.login,
                                    name: userData.name,
                                    email: userData.email,
                                    role: userData.role,
                                    auth_type: userData.auth_type,
                                    groups: userData.groups,
                                    description: userData.description,
                                    last_login: new Date().toISOString()
                                });
                            }
                        }
                    );
                } else {
                    // Создаем нового пользователя
                    this.db.run(
                        `INSERT INTO users (login, name, email, role, auth_type, groups, description, created_at, last_login) 
                         VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`,
                        [userData.login, userData.name, userData.email, userData.role, userData.auth_type, 
                         userData.groups, userData.description],
                        function(err) {
                            if (err) {
                                reject(err);
                            } else {
                                console.log(`✅ Создан новый LDAP пользователь ${username}. Роль: ${userData.role}, Группы: ${userGroups.length}`);
                                resolve({
                                    id: this.lastID,
                                    login: userData.login,
                                    name: userData.name,
                                    email: userData.email,
                                    role: userData.role,
                                    auth_type: userData.auth_type,
                                    groups: userData.groups,
                                    description: userData.description,
                                    last_login: new Date().toISOString()
                                });
                            }
                        }
                    );
                }
            });
        });
    }

    async authenticate(login, password) {
        if (!this.db) {
            throw new Error('База данных не инициализирована в AuthService');
        }

        // Сначала пробуем локальную авторизацию
        try {
            const localUser = await this.authenticateLocal(login, password);
            if (localUser) {
                return localUser;
            }
        } catch (error) {
            console.error('Ошибка локальной аутентификации:', error.message);
        }

        // Если локальная не сработала, пробуем LDAP
        try {
            const ldapUser = await this.authenticateLDAP(login, password);
            if (ldapUser) {
                return ldapUser;
            }
        } catch (error) {
            console.error('Ошибка LDAP аутентификации:', error.message);
        }

        return null;
    }

    getUserById(id) {
        if (!this.db) {
            throw new Error('База данных не инициализирована в AuthService');
        }

        return new Promise((resolve, reject) => {
            this.db.get("SELECT id, login, name, email, role, auth_type, groups, description, last_login FROM users WHERE id = ?", [id], (err, user) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(user);
                }
            });
        });
    }

    // Метод для проверки готовности сервиса
    isReady() {
        return this.db !== null;
    }
}

// Создаем singleton экземпляр
const authService = new AuthService();

module.exports = authService;