from flask import Flask, render_template, request, redirect, url_for, session, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from functools import wraps

app = Flask(__name__)
app.secret_key = 'super_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///portal.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

# --- МОДЕЛИ ---
class Admin(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password_hash = db.Column(db.String(255), nullable=False)

class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), unique=True, nullable=False)
    employees = db.relationship('Employee', backref='group', lazy=True)

class Employee(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    full_name = db.Column(db.String(150), nullable=False)
    corp_email = db.Column(db.String(120))
    edu_email = db.Column(db.String(120))
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    
    # СВЯЗИ (Один-ко-Многим)
    rustdesk_accounts = db.relationship('RustdeskAccount', backref='employee', lazy=True, cascade="all, delete-orphan")
    credentials = db.relationship('Credential', backref='employee', lazy=True, cascade="all, delete-orphan")

class RustdeskAccount(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'), nullable=False)
    computer_name = db.Column(db.String(100), nullable=False)
    rustdesk_id = db.Column(db.String(50), nullable=False)
    password = db.Column(db.String(100))

class Credential(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'), nullable=False)
    site_name = db.Column(db.String(100), nullable=False)
    url = db.Column(db.String(255))
    login = db.Column(db.String(100))
    password = db.Column(db.String(255))
    note = db.Column(db.String(255))
    
class QuickLink(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    url = db.Column(db.String(255), nullable=False)
    emoji = db.Column(db.String(10))
    color_class = db.Column(db.String(50), default='is-primary')

# --- МОДЕЛИ ДЛЯ ЗДАНИЙ И ИНФРАСТРУКТУРЫ ---
class Building(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    mesh_id = db.Column(db.String(50))
    name = db.Column(db.String(150), nullable=False)
    address = db.Column(db.String(255))
    
    # Связи (при удалении здания удаляются и все его настройки)
    networks = db.relationship('NetworkSetting', backref='building', lazy=True, cascade="all, delete-orphan")
    wifis = db.relationship('WifiNetwork', backref='building', lazy=True, cascade="all, delete-orphan")
    dvrs = db.relationship('VideoSurveillance', backref='building', lazy=True, cascade="all, delete-orphan")

class NetworkSetting(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    building_id = db.Column(db.Integer, db.ForeignKey('building.id'), nullable=False)
    vlan_name = db.Column(db.String(100))
    vlan_number = db.Column(db.Integer)
    network = db.Column(db.String(50))
    mask = db.Column(db.String(50))
    gateway = db.Column(db.String(50))
    dns1 = db.Column(db.String(50))
    dns2 = db.Column(db.String(50))
    static_ranges = db.Column(db.Text)
    ip_count = db.Column(db.String(100))

class WifiNetwork(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    building_id = db.Column(db.Integer, db.ForeignKey('building.id'), nullable=False)
    ssid = db.Column(db.String(100))
    password = db.Column(db.String(100))

class VideoSurveillance(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    building_id = db.Column(db.Integer, db.ForeignKey('building.id'), nullable=False)
    dvr_name = db.Column(db.String(100))
    location = db.Column(db.String(150))
    serial_number = db.Column(db.String(100))
    accounts = db.relationship('DvrAccount', backref='dvr', lazy=True, cascade="all, delete-orphan")

class DvrAccount(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    dvr_id = db.Column(db.Integer, db.ForeignKey('video_surveillance.id'), nullable=False)
    login = db.Column(db.String(100))
    password = db.Column(db.String(255))

# --- ДЕКОРАТОР ---
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'admin_id' not in session: 
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

# --- МАРШРУТЫ АДМИНОВ И ГРУПП ---
@app.route('/settings/group/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def group_edit(id):
    group = Group.query.get_or_404(id)
    if request.method == 'POST':
        group.name = request.form['name']
        db.session.commit()
        flash('Группа успешно переименована.', 'success')
        return redirect(url_for('settings'))
    return render_template('edit_group.html', group=group)

@app.route('/settings/admin/<int:admin_id>/edit', methods=['POST'])
def edit_admin(admin_id):
    if 'admin_id' not in session:
        return redirect('/login')
        
    admin = Admin.query.get_or_404(admin_id)
    # Используем .get(), чтобы избежать ошибки 400 Bad Request
    new_username = request.form.get('admin_username')
    new_password = request.form.get('admin_password')
    
    if new_username:
        admin.username = new_username
    if new_password: # Обновляем пароль, только если его ввели
        from werkzeug.security import generate_password_hash
        admin.password_hash = generate_password_hash(new_password)
        
    db.session.commit()
    return redirect('/settings')

@app.route('/settings/admin/<int:admin_id>/delete', methods=['POST'])
def delete_admin(admin_id):
    if 'admin_id' not in session:
        return redirect('/login')
        
    # ЖЕСТКИЙ ЗАПРЕТ: нельзя удалить самого себя
    if admin_id == session.get('admin_id'):
        return "Нельзя удалить самого себя", 403
        
    admin = Admin.query.get_or_404(admin_id)
    db.session.delete(admin)
    db.session.commit()
    return '', 200

@app.route('/settings/admin/add', methods=['POST'])
def add_admin():
    if 'admin_id' not in session:
        return redirect('/login')
        
    username = request.form.get('admin_username')
    password = request.form.get('admin_password')
    
    if username and password:
        # Проверяем, чтобы логин был уникальным
        existing_admin = Admin.query.filter_by(username=username).first()
        if not existing_admin:
            from werkzeug.security import generate_password_hash
            new_admin = Admin(
                username=username, 
                password_hash=generate_password_hash(password)
            )
            db.session.add(new_admin)
            db.session.commit()
            
    return redirect('/settings')


@app.route('/settings/link/add', methods=['POST'])
def add_link():
    if 'admin_id' not in session:
        return redirect(url_for('login'))
        
    title = request.form.get('link_title')
    url = request.form.get('link_url')
    
    # Защита от пустых строк
    color = request.form.get('link_color')
    if not color or color.strip() == '':
        color = '#3498db'
        
    emoji = request.form.get('link_emoji')
    if not emoji or emoji.strip() == '':
        emoji = '🔗'
    
    if title and url:
        # ИСПОЛЬЗУЕМ ПРАВИЛЬНУЮ МОДЕЛЬ: QuickLink и color_class
        new_link = QuickLink(
            title=title, 
            url=url, 
            color_class=color, 
            emoji=emoji
        )
        db.session.add(new_link)
        db.session.commit()
        
    return redirect(url_for('settings'))

@app.route('/settings/link/<int:link_id>/edit', methods=['POST'])
def edit_link(link_id):
    if 'admin_id' not in session:
        return redirect(url_for('login'))
        
    # ИСПОЛЬЗУЕМ ПРАВИЛЬНУЮ МОДЕЛЬ: QuickLink
    link = QuickLink.query.get_or_404(link_id)
    
    link.title = request.form.get('link_title')
    link.url = request.form.get('link_url')
    link.color_class = request.form.get('link_color')
    link.emoji = request.form.get('link_emoji')
    
    db.session.commit()
    return redirect(url_for('settings'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user = Admin.query.filter_by(username=request.form['username']).first()
        if user and check_password_hash(user.password_hash, request.form['password']):
            session['admin_id'] = user.id
            flash('Успешный вход в систему', 'success')
            return redirect(url_for('employees'))
        flash('Ошибка авторизации. Проверьте логин и пароль.', 'danger')
    return render_template('login.html')

@app.route('/logout')
def logout():
    session.clear()
    flash('Вы вышли из системы.', 'info')
    return redirect(url_for('login'))

# --- МАРШРУТЫ СОТРУДНИКОВ ---
@app.route('/employees', methods=['GET', 'POST']) # <-- Добавлено разрешние на POST-запросы
@login_required
def employees():
    # Если форма отправила POST-запрос (нажата кнопка добавления группы)
    if request.method == 'POST':
        if 'add_group' in request.form:
            group_name = request.form.get('group_name')
            if group_name:
                # Проверяем, нет ли уже группы с таким именем, чтобы избежать ошибки уникальности
                existing_group = Group.query.filter_by(name=group_name).first()
                if not existing_group:
                    new_group = Group(name=group_name)
                    db.session.add(new_group)
                    db.session.commit()
                    flash('Группа успешно добавлена!', 'success')
                else:
                    flash('Группа с таким названием уже существует.', 'danger')
            return redirect(url_for('employees'))

    # Обычный GET-запрос: загружаем списки
    groups = Group.query.all()
    no_group = Employee.query.filter_by(group_id=None).all()
    return render_template('employees.html', groups=groups, no_group=no_group)

@app.route('/employee/<int:id>')
@login_required
def employee_detail(id):
    emp = Employee.query.get_or_404(id)
    groups = Group.query.all()
    return render_template('employee_detail.html', emp=emp, groups=groups)

@app.route('/employee/add', methods=['GET', 'POST'])
@app.route('/employee/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def employee_form(id=None):
    emp = Employee.query.get(id) if id else None
    groups = Group.query.all()
    
    if request.method == 'POST':
        full_name = request.form.get('full_name', '')
        corp_email = request.form.get('corp_email', '')
        edu_email = request.form.get('edu_email', '')
        gid = request.form.get('group_id')
        
        if not emp:
            emp = Employee()
            db.session.add(emp)
        
        emp.full_name = full_name
        emp.corp_email = corp_email
        emp.edu_email = edu_email
        emp.group_id = int(gid) if gid else None
        
        db.session.commit()

        # Если с формы (например, старой версии) пришел RustDesk, создаем учетку
        raw_rd_id = request.form.get('rustdesk_id')
        if raw_rd_id:
            rd_id = raw_rd_id.replace(" ", "")
            if rd_id.isdigit():
                rd = RustdeskAccount(
                    employee_id=emp.id,
                    computer_name="Основной ПК",
                    rustdesk_id=rd_id,
                    password=request.form.get('rustdesk_password', '')
                )
                db.session.add(rd)
                db.session.commit()
            else:
                flash('Профиль сохранен, но RustDesk ID пропущен (должен содержать только цифры).', 'warning')
        
        flash('Данные сотрудника сохранены.', 'success')
        return redirect(url_for('employee_detail', id=emp.id))
        
    return render_template('employee_form.html', emp=emp, groups=groups)

@app.route('/employee/<int:id>/delete', methods=['POST'])
@login_required
def employee_delete(id):
    db.session.delete(Employee.query.get_or_404(id))
    db.session.commit()
    flash('Сотрудник удален.', 'success')
    return redirect(url_for('employees'))

# --- МАРШРУТЫ НАСТРОЕК ---
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    if request.method == 'POST':
        if 'add_group' in request.form:
            db.session.add(Group(name=request.form['group_name']))
            flash('Группа добавлена.', 'success')
        elif 'add_admin' in request.form:
            pw = generate_password_hash(request.form['admin_password'])
            db.session.add(Admin(username=request.form['admin_username'], password_hash=pw))
            flash('Администратор добавлен.', 'success')
        elif 'add_link' in request.form:
            new_link = QuickLink(
                title=request.form['link_title'],
                url=request.form['link_url'],
                emoji=request.form['link_emoji'],
                color_class=request.form['link_color']
            )
            db.session.add(new_link)
            flash('Быстрая ссылка добавлена.', 'success')
        db.session.commit()
        return redirect(url_for('settings'))
    
    return render_template('settings.html', 
                           groups=Group.query.all(), 
                           admins=Admin.query.all(),
                           links=QuickLink.query.all())

@app.route('/settings/link/<int:id>/delete', methods=['POST'])
@login_required
def link_delete(id):
    db.session.delete(QuickLink.query.get_or_404(id))
    db.session.commit()
    return redirect(url_for('settings'))

@app.route('/settings/group/<int:id>/delete', methods=['POST'])
@login_required
def group_delete(id):
    db.session.delete(Group.query.get_or_404(id))
    db.session.commit()
    return redirect(url_for('settings'))

# --- МАРШРУТЫ ЗДАНИЙ И ИНФРАСТРУКТУРЫ ---
@app.route('/buildings', methods=['GET', 'POST'])
@login_required
def buildings():
    if request.method == 'POST':
        new_building = Building(
            mesh_id=request.form.get('mesh_id'),
            name=request.form['name'],
            address=request.form.get('address')
        )
        db.session.add(new_building)
        db.session.commit()
        flash('Здание успешно добавлено.', 'success')
        return redirect(url_for('buildings'))
    
    all_buildings = Building.query.all()
    return render_template('buildings.html', buildings=all_buildings)

@app.route('/building/<int:id>/delete', methods=['POST'])
@login_required
def delete_building(id):
    building = Building.query.get_or_404(id)
    db.session.delete(building)
    db.session.commit()
    flash('Здание и вся его инфраструктура удалены.', 'success')
    return redirect(url_for('buildings'))

@app.route('/building/<int:id>')
@login_required
def building_detail(id):
    building = Building.query.get_or_404(id)
    return render_template('building_detail.html', b=building)

@app.route('/building/<int:id>/network/add', methods=['POST'])
@login_required
def add_network(id):
    Building.query.get_or_404(id)
    net = NetworkSetting(
        building_id=id,
        vlan_name=request.form.get('vlan_name'),
        vlan_number=request.form.get('vlan_number'),
        network=request.form.get('network'),
        mask=request.form.get('mask'),
        gateway=request.form.get('gateway'),
        dns1=request.form.get('dns1'),
        dns2=request.form.get('dns2'),
        static_ranges=request.form.get('static_ranges'),
        ip_count=request.form.get('ip_count')
    )
    db.session.add(net)
    db.session.commit()
    return redirect(url_for('building_detail', id=id))

@app.route('/building/<int:id>/wifi/add', methods=['POST'])
@login_required
def add_wifi(id):
    wifi = WifiNetwork(building_id=id, ssid=request.form.get('ssid'), password=request.form.get('password'))
    db.session.add(wifi)
    db.session.commit()
    return redirect(url_for('building_detail', id=id))

@app.route('/building/<int:id>/dvr/add', methods=['POST'])
@login_required
def add_dvr(id):
    dvr = VideoSurveillance(
        building_id=id,
        dvr_name=request.form.get('dvr_name'),
        location=request.form.get('location'),
        serial_number=request.form.get('serial_number')
    )
    db.session.add(dvr)
    db.session.commit()
    return redirect(url_for('building_detail', id=id))

@app.route('/dvr/<int:dvr_id>/account/add', methods=['POST'])
@login_required
def add_dvr_account(dvr_id):
    dvr = VideoSurveillance.query.get_or_404(dvr_id)
    acc = DvrAccount(dvr_id=dvr_id, login=request.form.get('login'), password=request.form.get('password'))
    db.session.add(acc)
    db.session.commit()
    return redirect(url_for('building_detail', id=dvr.building_id))

@app.route('/delete_item/<item_type>/<int:id>', methods=['POST'])
@login_required
def delete_item(item_type, id):
    item = None
    b_id = None
    if item_type == 'network':
        item = NetworkSetting.query.get_or_404(id)
        b_id = item.building_id
    elif item_type == 'wifi':
        item = WifiNetwork.query.get_or_404(id)
        b_id = item.building_id
    elif item_type == 'dvr':
        item = VideoSurveillance.query.get_or_404(id)
        b_id = item.building_id
    elif item_type == 'dvr_account':
        item = DvrAccount.query.get_or_404(id)
        b_id = item.dvr.building_id

    if item:
        db.session.delete(item)
        db.session.commit()
    return redirect(url_for('building_detail', id=b_id))

# --- ДОБАВЛЕНИЕ И УДАЛЕНИЕ RUSTDESK ---
@app.route('/employee/<int:id>/rustdesk/add', methods=['POST'])
@login_required
def add_rustdesk(id):
    Employee.query.get_or_404(id)
    
    # Получаем строку и оставляем в ней только цифры
    raw_id = request.form.get('rustdesk_id', '')
    clean_id = ''.join(filter(str.isdigit, raw_id))
    
    rd = RustdeskAccount(
        employee_id=id,
        computer_name=request.form.get('computer_name'),
        rustdesk_id=clean_id,
        password=request.form.get('password')
    )
    db.session.add(rd)
    db.session.commit()
    flash('Учетная запись RustDesk добавлена!', 'success')
    return redirect(url_for('employee_detail', id=id))

@app.route('/rustdesk/<int:id>/delete', methods=['POST'])
@login_required
def delete_rustdesk(id):
    rd = RustdeskAccount.query.get_or_404(id)
    emp_id = rd.employee_id
    db.session.delete(rd)
    db.session.commit()
    return redirect(url_for('employee_detail', id=emp_id))

@app.route('/rustdesk/<int:id>/edit', methods=['POST'])
@login_required
def edit_rustdesk(id):
    # Находим запись
    rd = RustdeskAccount.query.get_or_404(id)
    
    # Получаем строку и оставляем в ней только цифры
    raw_id = request.form.get('rustdesk_id', '')
    clean_id = ''.join(filter(str.isdigit, raw_id))
    
    # Обновляем данные
    rd.computer_name = request.form.get('computer_name')
    rd.rustdesk_id = clean_id
    rd.password = request.form.get('password')
    
    # Сохраняем в базу
    db.session.commit()
    flash('Учетная запись RustDesk успешно обновлена!', 'success')
    
    # Возвращаемся обратно в профиль сотрудника
    return redirect(url_for('employee_detail', id=rd.employee_id))


@app.route('/credential/<int:id>/edit', methods=['POST'])
@login_required
def edit_credential(id):
    # Находим запись
    cred = Credential.query.get_or_404(id)
    
    # Обновляем данные
    cred.site_name = request.form.get('site_name')
    cred.url = request.form.get('url')
    cred.login = request.form.get('login')
    cred.password = request.form.get('password')
    cred.note = request.form.get('note')
    
    # Сохраняем в базу
    db.session.commit()
    flash('Доступ к сайту успешно обновлен!', 'success')
    
    # Возвращаемся обратно в профиль сотрудника
    return redirect(url_for('employee_detail', id=cred.employee_id))

# --- ДОБАВЛЕНИЕ И УДАЛЕНИЕ УЧЕТНЫХ ЗАПИСЕЙ САЙТОВ ---
@app.route('/employee/<int:id>/credential/add', methods=['POST'])
@login_required
def add_credential(id):
    Employee.query.get_or_404(id)
    cred = Credential(
        employee_id=id,
        site_name=request.form.get('site_name'),
        url=request.form.get('url'),
        login=request.form.get('login'),
        password=request.form.get('password'),
        note=request.form.get('note')
    )
    db.session.add(cred)
    db.session.commit()
    flash('Данные сервиса добавлены!', 'success')
    return redirect(url_for('employee_detail', id=id))

@app.route('/credential/<int:id>/delete', methods=['POST'])
@login_required
def delete_credential(id):
    cred = Credential.query.get_or_404(id)
    emp_id = cred.employee_id
    db.session.delete(cred)
    db.session.commit()
    return redirect(url_for('employee_detail', id=emp_id))

# --- ГЛОБАЛЬНЫЙ КОНТЕКСТ ---
@app.context_processor
def inject_admin():
    if 'admin_id' in session:
        return {'current_admin': Admin.query.get(session['admin_id'])}
    return {}

@app.route('/')
@login_required
def index():
    links = QuickLink.query.all()
    return render_template('index.html', links=links)


# === ИНИЦИАЛИЗАЦИЯ БАЗЫ ДАННЫХ ===
with app.app_context():
    db.create_all()
    # Проверяем, есть ли в базе ХОТЯ БЫ ОДИН администратор.
    # Если нет (count == 0), то создаем дефолтного.
    if Admin.query.count() == 0:
        from werkzeug.security import generate_password_hash
        db.session.add(Admin(username='admin', password_hash=generate_password_hash('admin123')))
        db.session.commit()


# === ЗАПУСК ДЛЯ РЕЖИМА РАЗРАБОТКИ ===
if __name__ == '__main__':
    # Gunicorn проигнорирует этот блок, но он сработает, если запустить `python app.py` на локальном ПК
    app.run(host='0.0.0.0', port=5000, debug=True)