from flask import render_template, request, redirect, url_for, session, send_file, jsonify, flash
from werkzeug.security import generate_password_hash, check_password_hash
import sqlite3
from datetime import datetime
from io import BytesIO
from fpdf import FPDF
import time
import json
import logging
import os
from user_management import user_manager
from tcp_manager import tcp_manager
from translations import TRANSLATIONS
import threading

logger = logging.getLogger(__name__)

# Flask'ın flash fonksiyonunu kullan (override kaldırıldı)

def get_db_connection():
    conn = sqlite3.connect('data.db')
    conn.row_factory = sqlite3.Row
    return conn

def get_locale():
    return session.get('dil', 'tr')

def _(key):
    lang = get_locale()
    return TRANSLATIONS.get(lang, TRANSLATIONS['tr']).get(key, key)

def init_routes(app):
    """Tüm route'ları Flask app'e ekle"""
    
    @app.template_filter('datetime')
    def jinja_datetime(value):
        try:
            # Değer zaten string ise direkt döndür
            if isinstance(value, str):
                return value
            # SQLite datetime objesi ya da timestamp ise stringe çevir
            return str(value)
        except Exception:
            return str(value)
    
    # Ana sayfa route'u
    @app.route('/')
    def home():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        # Geçiş listesi başlangıç
        gecisler = []
        
        # Cihaz sayısını hesapla
        total_devices = 0
        if is_admin:
            conn = get_db_connection()
            # Ana veritabanındaki cihazları say
            data_devices = conn.execute('SELECT COUNT(*) as count FROM cihazlar').fetchone()
            total_devices += data_devices['count'] if data_devices else 0
            
            # Kullanıcı cihazlarını da say (global user_manager - data.db)
            try:
                user_devices = user_manager.get_all_user_devices()
                total_devices += len(user_devices)
            except Exception as e:
                logger.error(f"Kullanıcı cihazları sayma hatası: {e}")

        else:
            # Normal kullanıcı için sadece kendi cihazlarını say
            try:
                user_devices = user_manager.get_user_devices(user_id)
                total_devices = len(user_devices)
            except Exception as e:
                logger.error(f"Kullanıcı cihazları sayma hatası: {e}")
                total_devices = 0
            
            # Önce kullanıcı bazlı logları dene
            user_logs = user_manager.get_user_access_logs(user_id, limit=1000)
            gecisler = []
            for r in user_logs:
                gecisler.append({
                    'id': None,
                    'device_id': r['device_id'],
                    'user_name': r['person_name'],
                    'device_name': r['region'],
                    'card_uuid': '',
                    'access_type': 3,  # TCP SERVER
                    'is_granted': 1 if (r['access_status'] or '').lower() == 'granted' else 0,
                    'timestamp': r['access_time'],
                    'notes': r['notes'] or '',
                    'is_terminal': 2,
                    'source': 'new'
                })
            
            # Kullanıcının cihazları için merkezi access_logs kayıtlarını da ekle
            try:
                device_serials = [str(d['serial_number']) for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])]
                if device_serials:
                    conn = get_db_connection()
                    placeholders = ','.join(['?'] * len(device_serials))
                    rows = conn.execute(f'''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                             FROM access_logs 
                                             WHERE device_id IN ({placeholders})
                                             ORDER BY timestamp DESC
                                             LIMIT 1000''', device_serials).fetchall()
                    conn.close()
                    # Cihaz isimlerini haritalandır
                    device_name_map = {str(d['serial_number']): (d['device_name'] if 'device_name' in d.keys() else '') for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])}
                    # UUID->İsim haritası
                    uuid_to_name = {}
                    try:
                        dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                        for ur in dconn.execute('SELECT user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                            if ur['user_uuid']:
                                uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                        dconn.close()
                    except Exception:
                        pass
                    try:
                        uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                        for ur in uconn.execute('SELECT user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                            if ur['user_uuid'] and str(ur['user_uuid']) not in uuid_to_name:
                                uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                        uconn.close()
                    except Exception:
                        pass
                    # card_uuid -> name mapping (hex/dec normalize)
                    def build_user_map():
                        m = {}
                        def add(uval, name):
                            s = str(uval or '').strip().lower()
                            if not s or s == 'mastercard':
                                return
                            try:
                                if s not in m: m[s] = name or ''
                                if s.startswith('0x'):
                                    n = int(s,16); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x')
                                    for k in (decs,hx,hx8):
                                        if k not in m: m[k]=name or ''
                                elif s.isdigit():
                                    n = int(s,10); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x')
                                    for k in (decs,hx,hx8):
                                        if k not in m: m[k]=name or ''
                                else:
                                    import re
                                    if re.fullmatch(r'[0-9a-f]+', s):
                                        n=int(s,16); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x'); preserved='0x'+s
                                        for k in (preserved,decs,hx,hx8):
                                            if k not in m: m[k]=name or ''
                            except Exception:
                                pass
                        try:
                            d=sqlite3.connect('data.db'); d.row_factory=sqlite3.Row
                            for ur in d.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                                add(ur['card_uuid'], ur['full_name'])
                            d.close()
                        except Exception:
                            pass
                        try:
                            u=sqlite3.connect('users.db'); u.row_factory=sqlite3.Row
                            for ur in u.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                                add(ur['card_uuid'], ur['full_name'])
                            u.close()
                        except Exception:
                            pass
                        return m
                    user_map = build_user_map()
                    for r in rows:
                        note = ('Cihaz Verisi' if (str(r['is_terminal']) != '2') else '')
                        cu_key = str(r['card_uuid']).strip().lower()
                        name = user_map.get(cu_key, '')
                        if not name and cu_key:
                            try:
                                if cu_key.startswith('0x'):
                                    n=int(cu_key,16); alt=[str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                                elif cu_key.isdigit():
                                    n=int(cu_key,10); alt=[str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                                else:
                                    import re
                                    if re.fullmatch(r'[0-9a-f]+', cu_key):
                                        n=int(cu_key,16); alt=['0x'+cu_key,'0x'+format(n,'x'),'0x'+format(n,'08x'),str(n)]
                                    else:
                                        alt=[]
                                for k in alt:
                                    if k in user_map:
                                        name = user_map[k]; break
                            except Exception:
                                pass
                        item = {
                            'id': r['id'],
                            'device_id': str(r['device_id']),
                            'user_name': name,
                            'device_name': device_name_map.get(str(r['device_id']), ''),
                            'card_uuid': r['card_uuid'],
                            'access_type': r['access_type'],
                            'is_granted': r['is_granted'],
                            'timestamp': r['timestamp'],
                            'notes': note,
                            'is_terminal': r['is_terminal'],
                            'source': 'central'
                        }
                        # BUTTON özel kuralı
                        try:
                            cu = str(item.get('card_uuid') or '').strip().upper()
                            if cu in ('BUTTON-1','BUTTON-2'):
                                base = item['device_name'] or str(item['device_id'])
                                item['user_name'] = f"{base} BUTTON"
                        except Exception:
                            pass
                        # Mobil notu varsa zenginleştir
                        try:
                            if str(item.get('access_type')) == '3' and str(item.get('is_terminal', '0')) == '2' and item.get('id'):
                                _m = sqlite3.connect('data.db'); _m.row_factory = sqlite3.Row
                                _mr = _m.execute('SELECT note FROM mobile_access_notes WHERE access_log_id = ?', (item['id'],)).fetchone()
                                _m.close()
                                if _mr and _mr['note']:
                                    item['notes'] = _mr['note']
                        except Exception:
                            pass
                        gecisler.append(item)
            except Exception:
                pass

        # Admin: mobil (is_terminal=2) TCP SERVER kayıtları için notları toplu çek ve uygula
        try:
            ids_to_fill = [int(i['id']) for i in gecisler if i.get('id') and str(i.get('access_type'))=='3' and str(i.get('is_terminal'))=='2' and not (i.get('notes') or '').strip()]
            if ids_to_fill:
                q = ','.join(['?'] * len(ids_to_fill))
                mconn = sqlite3.connect('data.db'); mconn.row_factory = sqlite3.Row
                rows = mconn.execute(f'SELECT access_log_id, note FROM mobile_access_notes WHERE access_log_id IN ({q})', ids_to_fill).fetchall()
                mconn.close()
                note_map = {int(r['access_log_id']): (r['note'] or '') for r in rows}
                for it in gecisler:
                    try:
                        if it.get('id') and int(it['id']) in note_map and str(it.get('access_type'))=='3' and str(it.get('is_terminal'))=='2':
                            it['notes'] = note_map[int(it['id'])]
                    except Exception:
                        continue
        except Exception:
            pass

        # UUID -> Ad toplu eşlemesi (admin): user_name boş, card_uuid dolu kayıtlar için
        try:
            unresolved = list({str(i['card_uuid']) for i in gecisler if (i.get('card_uuid') and not i.get('user_name'))})
            if unresolved:
                name_map = {}
                q_marks = ','.join(['?'] * len(unresolved))
                # data.db
                try:
                    dconnx = sqlite3.connect('data.db'); dconnx.row_factory = sqlite3.Row
                    for ur in dconnx.execute(f'SELECT user_uuid, full_name FROM users WHERE user_uuid IN ({q_marks})', unresolved):
                        if ur['user_uuid'] and ur['full_name']:
                            name_map[str(ur['user_uuid'])] = ur['full_name']
                    dconnx.close()
                except Exception:
                    pass
                # users.db
                try:
                    uconnx = sqlite3.connect('users.db'); uconnx.row_factory = sqlite3.Row
                    for ur in uconnx.execute(f'SELECT user_uuid, full_name FROM users WHERE user_uuid IN ({q_marks})', unresolved):
                        if ur['user_uuid'] and ur['full_name'] and str(ur['user_uuid']) not in name_map:
                            name_map[str(ur['user_uuid'])] = ur['full_name']
                        uconnx.close()
                except Exception:
                    pass
                for it in gecisler:
                    if it.get('card_uuid') and not it.get('user_name'):
                        it['user_name'] = name_map.get(str(it['card_uuid']), it.get('user_name',''))
        except Exception:
            pass

        # Ek: user_name boş ve card_uuid dolu kayıtlar için toplu UUID->ad çözümleme
        try:
            unresolved = list({str(i['card_uuid']) for i in gecisler if (i.get('card_uuid') and not i.get('user_name'))})
            if unresolved:
                name_map = {}
                # data.db
                dconnx = sqlite3.connect('data.db'); dconnx.row_factory = sqlite3.Row
                q = ','.join(['?'] * len(unresolved))
                for ur in dconnx.execute(f'SELECT user_uuid, full_name FROM users WHERE user_uuid IN ({q})', unresolved):
                    if ur['user_uuid'] and ur['full_name']:
                        name_map[str(ur['user_uuid'])] = ur['full_name']
                dconnx.close()
                # users.db
                uconnx = sqlite3.connect('users.db'); uconnx.row_factory = sqlite3.Row
                for ur in uconnx.execute(f'SELECT user_uuid, full_name FROM users WHERE user_uuid IN ({q})', unresolved):
                    if ur['user_uuid'] and ur['full_name'] and str(ur['user_uuid']) not in name_map:
                        name_map[str(ur['user_uuid'])] = ur['full_name']
                uconnx.close()
                for i in gecisler:
                    if i.get('card_uuid') and not i.get('user_name'):
                        i['user_name'] = name_map.get(str(i['card_uuid']), i.get('user_name',''))
                    gecisler.sort(key=lambda x: str(x.get('timestamp','')), reverse=True)
        except Exception:
            pass

        # Filtre uygula (tüm hücreler)
        try:
            query = (request.args.get('adsoyad', '') or '').strip().lower()
            if query:
                filtered = []
                for g in gecisler:
                    # Tür etiketleri
                    type_tokens = []
                    try:
                        at = str(g.get('access_type', '')).strip()
                        it = str(g.get('is_terminal', '')).strip()
                        if at in ['1','nfc']: type_tokens += ['nfc']
                        elif at in ['2','button','buton']: type_tokens += ['button','buton']
                        elif at in ['3']:
                            if it == '2':
                                type_tokens += ['mobil sayfa','mobil','tcp server']
                            else:
                                type_tokens += ['tcp server']
                        elif at in ['4']: type_tokens += ['tcp client']
                        elif at in ['5']: type_tokens += ['ble','bluetooth']
                        elif at in ['6']: type_tokens += ['rs485','rs-485']
                    except Exception:
                        pass
                    status_text = 'izin verildi' if g.get('is_granted') else 'izin verilmedi'
                    haystack = ' '.join([
                        str(g.get('user_name','')),
                        str(g.get('card_uuid','')),
                        str(g.get('device_name','')),
                        str(g.get('device_id','')),
                        str(g.get('notes','')),
                        ' '.join(type_tokens),
                        status_text,
                        str(g.get('timestamp',''))
                    ]).lower()
                    if query in haystack:
                        filtered.append(g)
                gecisler = filtered
        except Exception:
            pass

        total_access = len(gecisler)

        # Normal kullanıcı için sadece kendi cihazlarını say
        try:
            user_devices = user_manager.get_user_devices(user_id)
            total_devices = len(user_devices)
        except Exception as e:
            logger.error(f"Kullanıcı cihazları sayma hatası: {e}")
            total_devices = 0
        
        # Önce kullanıcı bazlı logları dene
        user_logs = user_manager.get_user_access_logs(user_id, limit=1000)
        gecisler = []
        for r in user_logs:
            gecisler.append({
                'id': None,
                'device_id': r['device_id'],
                'user_name': r['person_name'],
                'device_name': r['region'],
                'card_uuid': '',
                'access_type': 3,  # TCP SERVER
                'is_granted': 1 if (r['access_status'] or '').lower() == 'granted' else 0,
                'timestamp': r['access_time'],
                'notes': r['notes'] or '',
                'is_terminal': 2,
                'source': 'new'
            })
        # Her durumda: kullanıcı cihazları için merkezi access_logs'tan da (up to 1000) ekle ve birleştir
        try:
            device_serials = [str(d['serial_number']) for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])]
            if device_serials:
                conn = get_db_connection()
                placeholders = ','.join(['?'] * len(device_serials))
                rows = conn.execute(f'''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                        FROM access_logs 
                                        WHERE device_id IN ({placeholders})
                                        ORDER BY timestamp DESC
                                        LIMIT 1000''', device_serials).fetchall()
                conn.close()
                # Cihaz isimlerini haritalandır
                device_name_map = {str(d['serial_number']): (d['device_name'] if 'device_name' in d.keys() else '') for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])}
                # UUID->İsim haritası
                uuid_to_name = {}
                try:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    for ur in dconn.execute('SELECT user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid']:
                            uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                    dconn.close()
                except Exception:
                    pass
                try:
                    uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                    for ur in uconn.execute('SELECT user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid'] and str(ur['user_uuid']) not in uuid_to_name:
                            uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                        uconn.close()
                except Exception:
                    pass
                for r in rows:
                    note = ('Cihaz Verisi' if (str(r['is_terminal']) != '2') else '')
                    item = {
                        'id': r['id'],
                        'device_id': str(r['device_id']),
                        'user_name': uuid_to_name.get(str(r['card_uuid']), ''),
                        'device_name': device_name_map.get(str(r['device_id']), ''),
                        'card_uuid': r['card_uuid'],
                        'access_type': r['access_type'],
                        'is_granted': r['is_granted'],
                        'timestamp': r['timestamp'],
                        'notes': note,
                        'is_terminal': r['is_terminal'],
                        'source': 'central'
                    }
                    # BUTTON özel kuralı: UUID BUTTON-1/BUTTON-2 ise kullanıcı adı cihaz adı + " BUTTON"
                    try:
                        cu = str(item.get('card_uuid') or '').strip().upper()
                        if cu in ('BUTTON-1','BUTTON-2'):
                            base = item['device_name'] or str(item['device_id'])
                            item['user_name'] = f"{base} BUTTON"
                    except Exception:
                        pass
                    # Mobil notu varsa zenginleştir
                    try:
                        if str(item.get('access_type')) == '3' and str(item.get('is_terminal', '0')) == '2' and item.get('id'):
                            _m = sqlite3.connect('data.db'); _m.row_factory = sqlite3.Row
                            _mr = _m.execute('SELECT note FROM mobile_access_notes WHERE access_log_id = ?', (item['id'],)).fetchone()
                            _m.close()
                            if _mr and _mr['note']:
                                item['notes'] = _mr['note']
                                # access_type etiketi dashboard görünümünde doğru çıksın
                                item['access_type'] = 3
                    except Exception:
                        pass
                    gecisler.append(item)
            # Birleştirilmiş listeyi timestamp'e göre DESC sırala
            try:
                gecisler.sort(key=lambda x: str(x.get('timestamp','')), reverse=True)
            except Exception:
                pass
        except Exception as e:
            logger.error(f"Dashboard access_logs combine hatası: {e}")

        # Fallback: kullanıcı logu yoksa merkezi access_logs'tan kendi cihazlarını getir
        if len(gecisler) == 0 and total_devices > 0:
            try:
                device_serials = [str(d['serial_number']) for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])]
                if device_serials:
                    conn = get_db_connection()
                    placeholders = ','.join(['?'] * len(device_serials))
                    rows = conn.execute(f'''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                            FROM access_logs 
                                            WHERE device_id IN ({placeholders})
                                            ORDER BY timestamp DESC
                                            LIMIT 50''', device_serials).fetchall()
                    conn.close()
                    # Cihaz isimlerini haritalandır
                    device_name_map = {str(d['serial_number']): (d['device_name'] if 'device_name' in d.keys() else '') for d in user_devices if ('serial_number' in d.keys() and d['serial_number'])}
                    # UUID->İsim haritası
                    uuid_to_name = {}
                    try:
                        dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                        for ur in dconn.execute('SELECT user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                            if ur['user_uuid']:
                                uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                            uconn.close()
                        uconn.close()
                    except Exception:
                        pass
                    for r in rows:
                        note = ('Cihaz Verisi' if (str(r['is_terminal']) != '2') else '')
                        item = {
                            'id': r['id'],
                            'device_id': str(r['device_id']),
                            'user_name': uuid_to_name.get(str(r['card_uuid']), ''),
                            'device_name': device_name_map.get(str(r['device_id']), ''),
                            'card_uuid': r['card_uuid'],
                            'access_type': r['access_type'],
                            'is_granted': r['is_granted'],
                            'timestamp': r['timestamp'],
                            'notes': note,
                            'is_terminal': r['is_terminal'],
                            'source': 'central'
                        }
                        # BUTTON özel kuralı: UUID BUTTON-1/BUTTON-2 ise kullanıcı adı cihaz adı + " BUTTON"
                        try:
                            cu = str(item.get('card_uuid') or '').strip().upper()
                            if cu in ('BUTTON-1','BUTTON-2'):
                                base = item['device_name'] or str(item['device_id'])
                                item['user_name'] = f"{base} BUTTON"
                        except Exception:
                            pass
                        # Mobil notu varsa zenginleştir
                        try:
                            if str(item.get('access_type')) == '3' and str(item.get('is_terminal', '0')) == '2':
                                _m = sqlite3.connect('data.db'); _m.row_factory = sqlite3.Row
                                _mr = _m.execute('SELECT note FROM mobile_access_notes WHERE access_log_id = ?', (item['id'],)).fetchone()
                                _m.close()
                                if _mr and _mr['note']:
                                    item['notes'] = _mr['note']
                        except Exception:
                            pass
                        gecisler.append(item)
            except Exception as e:
                logger.error(f"Dashboard fallback access_logs hatası: {e}")
        total_access = len(gecisler)
    
        # Aktif cihaz sayısını hesapla
        active_devices = 0
        try:
            if is_admin:
                conn = get_db_connection()
                data_devices = conn.execute('SELECT * FROM cihazlar').fetchall()
                conn.close()
                
                for device in data_devices:
                    # Bağlantı anahtarı olarak seri_no mevcutsa onu kullan; yoksa id
                    key = str(device['seri_no'] if 'seri_no' in device.keys() and device['seri_no'] else device['id'])
                    if tcp_manager.is_device_connected(key):
                        active_devices += 1
                
                # Kullanıcı cihazlarını da kontrol et
                user_devices = user_manager.get_all_user_devices()
                for device in user_devices:
                    if tcp_manager.is_device_connected(str(device['serial_number'])):
                        active_devices += 1
            else:
                user_devices = user_manager.get_user_devices(user_id)
                for device in user_devices:
                    if tcp_manager.is_device_connected(str(device['serial_number'])):
                        active_devices += 1
        except Exception as e:
            logger.error(f"Aktif cihaz sayma hatası: {e}")
        
        total_pages = 1
        page = 1
        
        # Kullanıcı bilgilerini getir
        try:
            conn = get_db_connection()
            cursor = conn.cursor()
            cursor.execute('''
                SELECT email, full_name, company, phone, user_type, access_type, created_at, timezone, user_uuid, profile_image
                FROM users WHERE id = ?
            ''', (user_id,))
            user = cursor.fetchone()
            conn.close()
            
            if user:
                user_data = {
                    'email': user[0],
                    'full_name': user[1],
                    'company': user[2] or '',
                    'phone': user[3] or '',
                    'user_type': user[4] or 'standart',
                    'access_type': user[5] or 'mobil uygulama',
                    'created_at': user[6],
                    'timezone': user[7] or 'Europe/Istanbul',
                    'user_uuid': user[8],
                    'profile_image': user[9]
                }
            else:
                user_data = {}
            # data.db'de profile_image yoksa users.db'den doldur
            if (not user_data) or (not user_data.get('profile_image')):
                try:
                    uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                    urow = uconn.execute('SELECT email, full_name, company, phone, user_uuid, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                    uconn.close()
                    if urow:
                        if not user_data:
                            user_data = {}
                        user_data.setdefault('email', urow['email'])
                        user_data.setdefault('full_name', urow['full_name'])
                        user_data.setdefault('company', urow['company'] or '')
                        user_data.setdefault('phone', urow['phone'] or '')
                        user_data.setdefault('user_uuid', urow['user_uuid'])
                        user_data['profile_image'] = urow['profile_image']
                except Exception:
                    pass
        except Exception as e:
            logger.error(f"Kullanıcı bilgileri getirme hatası: {e}")
            user_data = {}
        
                # Son giriş zamanını al (önce data.db, sonra users.db)
        last_login_value = None
        try:
            dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
            drow = dconn.execute('SELECT last_login FROM users WHERE id = ?', (user_id,)).fetchone()
            dconn.close()
            if drow and drow['last_login']:
                last_login_value = str(drow['last_login']).split('.')[0]
        except Exception as e:
            pass
        if last_login_value is None:
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                urow = uconn.execute('SELECT last_login FROM users WHERE id = ?', (user_id,)).fetchone()
                uconn.close()
                if urow and urow['last_login']:
                    last_login_value = str(urow['last_login']).split('.')[0]
            except Exception as e:
                pass
        
        # Normal kullanıcı için geçiş kayıtlarını hazırla (user_access_logs) - daha önce hazırlı yoksa
        if not is_admin and (("gecisler" not in locals()) or len(gecisler) == 0):
            try:
                # Global user_manager (data.db) kullan
                src_logs = user_manager.get_user_access_logs(user_id, limit=1000)
                gecisler = []
                for r in src_logs:
                    gecisler.append({
                        'device_id': r['device_id'],
                        'user_name': r['person_name'],
                        'device_name': r['region'],
                        'card_uuid': '',
                        'access_type': 3,
                        'is_granted': 1 if (r['access_status'] or '').lower() == 'granted' else 0,
                        'timestamp': r['access_time'],
                        'source': 'new'
                    })
                # Fallback: kullanıcı logu yoksa merkezi access_logs'tan kendi cihazlarını getir
                if len(gecisler) == 0:
                    assigned = user_manager.get_user_devices(user_id)
                    device_serials = [str(d['serial_number']) for d in assigned if ('serial_number' in d.keys() and d['serial_number'])]
                    if device_serials:
                        conn = get_db_connection()
                        placeholders = ','.join(['?'] * len(device_serials))
                        rows = conn.execute(f'''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                                 FROM access_logs 
                                                 WHERE device_id IN ({placeholders})
                                                 ORDER BY timestamp DESC
                                                 LIMIT 50''', device_serials).fetchall()
                        conn.close()
                        device_name_map = {str(d['serial_number']): (d['device_name'] if 'device_name' in d.keys() else '') for d in assigned if ('serial_number' in d.keys() and d['serial_number'])}
                        for r in rows:
                            note = ('Cihaz Verisi' if (str(r['is_terminal']) != '2') else '')
                            item = {
                                'id': r['id'],
                                'device_id': str(r['device_id']),
                                'user_name': '',
                                'device_name': device_name_map.get(str(r['device_id']), ''),
                                'card_uuid': r['card_uuid'],
                                'access_type': r['access_type'],
                                'is_granted': r['is_granted'],
                                'timestamp': r['timestamp'],
                                'notes': note,
                                'is_terminal': r['is_terminal'],
                                'source': 'new'
                            }
                            # BUTTON özel kuralı: UUID BUTTON-1/BUTTON-2 ise kullanıcı adı cihaz adı + " BUTTON"
                            try:
                                cu = str(item.get('card_uuid') or '').strip().upper()
                                if cu in ('BUTTON-1','BUTTON-2'):
                                    base = item['device_name'] or str(item['device_id'])
                                    item['user_name'] = f"{base} BUTTON"
                            except Exception:
                                pass
                            # Mobil notu varsa zenginleştir
                            try:
                                if str(item.get('access_type')) == '3' and str(item.get('is_terminal', '0')) == '2':
                                    _m = sqlite3.connect('data.db'); _m.row_factory = sqlite3.Row
                                    _mr = _m.execute('SELECT note FROM mobile_access_notes WHERE access_log_id = ?', (item['id'],)).fetchone()
                                    _m.close()
                                    if _mr and _mr['note']:
                                        item['notes'] = _mr['note']
                            except Exception:
                                pass
                            gecisler.append(item)
            except Exception as e:
                logger.error(f"Dashboard user_access_logs hatası: {e}")
        
        # Sayfa başlığı için çeviri
        page_title = _('dashboard')
        
        # Render template
        if gecisler is None:
            gecisler = []
        user_name = (user_data.get('full_name') if isinstance(user_data, dict) else '') if 'user_data' in locals() else ''
        # Sayfalama parametreleri
        try:
            page = int(request.args.get('page', 1))
        except Exception:
            page = 1
        # Ana sayfa için sayfa başı 50, en fazla 5 sayfa
        if page > 5:
            page = 5
        per_page = 50
        # access-logs ile aynı kaynak: sadece yetkili cihazlar (admin: tümü, user: kendi cihazları)
        device_serials = None
        if not is_admin:
            try:
                user_devices = user_manager.get_user_devices(user_id)
                device_serials = [str(d['serial_number']) for d in user_devices if d.get('serial_number')]
            except Exception:
                device_serials = []
        logs, total_rows, page, per_page, total_pages = _prepare_access_logs_common(device_serials, page, per_page, request.args.get('adsoyad',''))
        # Toplam sayfa sayısını en fazla 5 ile sınırla
        total_pages_limited = min(total_pages, 5)
        # İstatistikler (mobil/web HARİÇ)
        try:
            stats_items = []
            for _g in logs:
                at = str(_g.get('access_type', '')).strip(); iterm = str(_g.get('is_terminal', '')).strip()
                if not (at == '3' and iterm == '2'):
                    stats_items.append(_g)
            total_access = len(stats_items)
            granted_count = sum(1 for _g in stats_items if int(_g.get('is_granted', 0)) == 1)
            denied_count = len(stats_items) - granted_count
        except Exception:
            total_access = 0; granted_count = 0; denied_count = 0
        try:
            logger.info(f"HOME DEBUG is_admin={is_admin}, total_devices={total_devices}, active_devices={active_devices}, total_access={total_access}, page={page}, per_page={per_page}, total_pages={total_pages_limited}, page_len={len(logs)}")
        except Exception:
            pass
        # Normal kullanıcılar için: kendi eklediği/atanmış cihazları listele ve sayıları buna göre göster
        my_devices = []
        if not is_admin:
            try:
                user_devices = user_manager.get_user_devices(user_id)
                # data.db'den seri_no -> id eşlemesi cache'i
                serial_to_id = {}
                try:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    for r in dconn.execute('SELECT id, seri_no FROM cihazlar'):
                        if r['seri_no']:
                            serial_to_id[str(r['seri_no'])] = str(r['id'])
                    dconn.close()
                except Exception:
                    serial_to_id = {}
                for d in user_devices:
                    serial = str(d.get('serial_number') or '')
                    # Bağlantı: önce seri_no ile, olmadı id ile dene
                    connected = False
                    try:
                        connected = tcp_manager.is_device_connected(serial)
                        if not connected and serial in serial_to_id:
                            connected = tcp_manager.is_device_connected(serial_to_id[serial])
                    except Exception:
                        connected = False
                    my_devices.append({
                        'device_name': d.get('device_name') or '',
                        'serial_number': serial,
                        'ip_address': d.get('ip_address') or '',
                        'device_type': d.get('device_type') or '',
                        'is_connected': bool(connected)
                    })
                # Aynı kullanıcı tarafından oluşturulmuş (is_active filtresiz) tüm cihazları ekle
                try:
                    uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                    rows_all = uconn.execute('SELECT device_name, device_type, ip_address, serial_number FROM user_devices WHERE user_id = ?', (user_id,)).fetchall()
                    uconn.close()
                    existing_serials = {md['serial_number'] for md in my_devices if md.get('serial_number')}
                    for r in rows_all:
                        serial = str(r['serial_number'] or '')
                        if serial in existing_serials:
                            continue
                        connected = False
                        try:
                            connected = tcp_manager.is_device_connected(serial)
                            if not connected and serial in serial_to_id:
                                connected = tcp_manager.is_device_connected(serial_to_id[serial])
                        except Exception:
                            connected = False
                        my_devices.append({
                            'device_name': r['device_name'] or '',
                            'serial_number': serial,
                            'ip_address': r['ip_address'] or '',
                            'device_type': r['device_type'] or '',
                            'is_connected': bool(connected)
                        })
                except Exception:
                    pass
                # Eğer user_manager boş döndüyse user_device_access üzerinden getir
                if len(my_devices) == 0:
                    try:
                        uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                        rows = uconn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
                        uconn.close()
                        assigned_ids = [str(r['device_id']) for r in rows]
                    except Exception:
                        assigned_ids = []
                    if assigned_ids:
                        # id/seri no normalize ederek cihazları çek
                        try:
                            dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                            # Numeric id'ler
                            id_list = [i for i in assigned_ids if i.isdigit()]
                            # Seri no listesi
                            serial_list = [i for i in assigned_ids if not i.isdigit()]
                            cihazlar = []
                            if id_list:
                                placeholders = ','.join(['?'] * len(id_list))
                                cihazlar += dconn.execute(f'SELECT id, ad, seri_no, tcp_tipi, ip_adresi FROM cihazlar WHERE id IN ({placeholders})', id_list).fetchall()
                            if serial_list:
                                placeholders = ','.join(['?'] * len(serial_list))
                                cihazlar += dconn.execute(f'SELECT id, ad, seri_no, tcp_tipi, ip_adresi FROM cihazlar WHERE seri_no IN ({placeholders})', serial_list).fetchall()
                            dconn.close()
                            for c in cihazlar:
                                serial = str(c['seri_no'] or c['id'])
                                connected = False
                                try:
                                    # Hem seri_no hem id ile dene
                                    connected = tcp_manager.is_device_connected(serial) or tcp_manager.is_device_connected(str(c['id']))
                                except Exception:
                                    connected = False
                                my_devices.append({
                                    'device_name': c['ad'] or '',
                                    'serial_number': serial,
                                    'ip_address': c['ip_adresi'] or '',
                                    'device_type': c['tcp_tipi'] or '',
                                    'is_connected': bool(connected)
                                })
                        except Exception:
                            pass
                # Sayıları kendi cihazlarına göre güncelle
                total_devices = len(my_devices)
                active_devices = sum(1 for md in my_devices if md['is_connected'])
            except Exception:
                my_devices = []
        return render_template('dashboard.html',
                               page_title=page_title,
                               total_devices=total_devices,
                               active_devices=active_devices,
                               total_access=total_access,
                               granted_count=granted_count,
                               denied_count=denied_count,
                               gecisler=logs,
                               page=page,
                               per_page=per_page,
                               total_pages=total_pages_limited,
                               recent_count=len(logs),
                               total_rows=total_rows,
                               total_logs=total_rows,
                               user=user_data,
                               user_name=user_name,
                               my_devices=my_devices,
                               last_login=last_login_value,
                               get_page_range=lambda current, total: range(1, min(total, 5)+1)
                               )

    # Dil değiştirme route'u
    @app.route('/set-lang/<lang>')
    def set_lang(lang):
        if lang not in TRANSLATIONS:
            lang = 'tr'
        session['dil'] = lang
        # İsteğe bağlı yönlendirme: ?next=mobile_login|mobile_dashboard gibi
        next_target = request.args.get('next', '').strip()
        try:
            if next_target == 'mobile_login':
                return redirect(url_for('mobile_login'))
            if next_target == 'mobile_dashboard':
                return redirect(url_for('mobile_dashboard'))
        except Exception:
            pass
        return redirect(request.referrer or url_for('home'))

    # Giriş route'u
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            email = (request.form.get('kullanici') or '').strip().lower()
            password = request.form['sifre']
            
            success, result = user_manager.authenticate_user(email, password)
            
            if success:
                session['logged_in'] = True
                session['user_id'] = result['id']
                session['user_email'] = result['email']
                display_name = (result.get('full_name') or '').strip()
                if not display_name or display_name.upper() == 'MASTERCARD':
                    display_name = result['email']
                session['user_name'] = display_name
                session['company'] = result['company']
                session['is_admin'] = result['is_admin']
                try:
                    # Giriş yapan kullanıcı için mobil UUID garanti et
                    user_manager.ensure_mobile_uuid(result['id'])
                except Exception:
                    pass
                flash(_('login_success'), "success")
                return redirect(url_for('home'))
            else:
                flash(_(result), "error")
        
        return render_template('login.html', 
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # Kayıt route'u
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        if request.method == 'POST':
            full_name = request.form.get('full_name', '').strip()
            email = request.form.get('email', '').strip()
            password = request.form.get('password', '')
            confirm_password = request.form.get('confirm_password', '')
            company = request.form.get('company', '').strip()
            phone = request.form.get('phone', '').strip()
            
            if not full_name or not email or password != confirm_password:
                flash('Lütfen tüm alanları doğru şekilde doldurun', "error")
                return render_template('register.html', 
                                     translations=TRANSLATIONS,
                                     current_language=session.get('dil', 'tr'))
            
            success, message = user_manager.register_user(email, password, full_name, company, phone)
            
            if success:
                flash(message, "success")
                return redirect(url_for('login'))
            else:
                flash(message, "error")
        
        return render_template('register.html', 
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # Çıkış route'u
    @app.route('/logout')
    def logout():
        session.clear()
        return redirect(url_for('login'))

    # Kullanıcılar route'u
    @app.route('/users')
    def users():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        logger.info(f"Users route - user_id: {user_id}, is_admin: {is_admin}")
        
        # Cihaz kontrolü - en az 1 cihaz olmalı
        total_devices = 0
        if is_admin:
            conn = get_db_connection()
            # Ana veritabanındaki cihazları say
            data_devices = conn.execute('SELECT COUNT(*) as count FROM cihazlar').fetchone()
            total_devices += data_devices['count'] if data_devices else 0
            conn.close()
            
            # Kullanıcı cihazlarını da say
            try:
                user_devices = user_manager.get_all_user_devices()
                total_devices += len(user_devices)
            except Exception as e:
                logger.error(f"Kullanıcı cihazları sayma hatası: {e}")
        else:
            # Normal kullanıcı için sadece kendi cihazlarını say
            try:
                user_devices = user_manager.get_user_devices(user_id)
                total_devices = len(user_devices)
            except Exception as e:
                logger.error(f"Kullanıcı cihazları sayma hatası: {e}")
                total_devices = 0
        
        # Mastercard kullanıcılarının garanti et: device_cards'ta user_name='mastercard' olan her kart için
        if is_admin:
            try:
                dconn_mc = get_db_connection(); dconn_mc.row_factory = sqlite3.Row
                mc_rows = dconn_mc.execute("SELECT device_id, card_uuid FROM device_cards WHERE lower(user_name)='mastercard'").fetchall()
                dconn_mc.close()
                if mc_rows:
                    uconn_mc = sqlite3.connect('users.db'); uconn_mc.row_factory = sqlite3.Row
                    cur = uconn_mc.cursor()
                    # user_device_access tablosu garanti
                    cur.execute('''
                        CREATE TABLE IF NOT EXISTS user_device_access (
                            id INTEGER PRIMARY KEY AUTOINCREMENT,
                            user_id INTEGER NOT NULL,
                            device_id TEXT NOT NULL
                        )
                    ''')
                    cur.execute('''
                        CREATE UNIQUE INDEX IF NOT EXISTS idx_user_device_unique
                        ON user_device_access(user_id, device_id)
                    ''')
                    for r in mc_rows:
                        dev_id = str(r['device_id']); cu = str(r['card_uuid'] or '').strip()
                        if not cu:
                            continue
                        row = cur.execute('SELECT id, full_name, access_type, email, password_hash FROM users WHERE card_uuid = ?', (cu,)).fetchone()
                        if row:
                            uid = row['id']
                            acc = (row['access_type'] or '')
                            if 'nfc' not in (acc or '').split(','):
                                new_acc = (acc + ',nfc').strip(',') if acc else 'nfc'
                                cur.execute('UPDATE users SET access_type = ? WHERE id = ?', (new_acc, uid))
                            # Sadece pseudo mastercard kullanıcıları için isim/ikon ataması yap
                            email_val = row['email'] or ''
                            pwd_val = row['password_hash'] or ''
                            if email_val.startswith('mastercard-') or pwd_val == 'no_login_required':
                                if (row['full_name'] or '').upper() != 'MASTERCARD':
                                    cur.execute('UPDATE users SET full_name = ? WHERE id = ?', ('MASTERCARD', uid))
                        else:
                            email = f"mastercard-{dev_id}@user.local"
                            cur.execute('''
                                INSERT INTO users (email, password_hash, full_name, company, phone, user_type, access_type, card_uuid, user_uuid, created_at, is_active, created_by)
                                VALUES (?, 'no_login_required', 'MASTERCARD', '', '', 'standart', 'nfc', ?, NULL, datetime('now'), 1, NULL)
                            ''', (email, cu))
                            uid = cur.lastrowid
                        # Cihaza ataması
                        cur.execute('INSERT OR IGNORE INTO user_device_access (user_id, device_id) VALUES (?, ?)', (uid, dev_id))
                    uconn_mc.commit(); uconn_mc.close()
                    # data.db yansıması (yalnızca eksik ise ekle)
                    try:
                        d2 = sqlite3.connect('data.db'); d2.row_factory = sqlite3.Row
                        c2 = d2.cursor()
                        for r in mc_rows:
                            dev_id = str(r['device_id']); cu = str(r['card_uuid'] or '').strip()
                            if not cu: continue
                            # users.db'deki id'yi al
                            udb = sqlite3.connect('users.db'); udb.row_factory = sqlite3.Row
                            urow = udb.execute('SELECT id FROM users WHERE card_uuid = ?', (cu,)).fetchone(); udb.close()
                            if urow:
                                uid2 = urow['id']
                                c2.execute('''
                                    INSERT OR REPLACE INTO users (id, full_name, email, password_hash, user_type, access_type, is_active, created_at, card_uuid)
                                    VALUES (?, 'MASTERCARD', ?, 'no_login_required', 'standart', 'nfc', 1, datetime('now'), ?)
                                ''', (uid2, f"mastercard-{dev_id}@user.local", cu))
                        d2.commit(); d2.close()
                    except Exception as mm:
                        logger.error(f"data.db mastercard mirror hatası: {mm}")
            except Exception as emc:
                logger.error(f"Mastercard kullanıcı garanti hatası: {emc}")
        
        # Eğer cihaz yoksa uyarı göster (şimdilik skip)
        # if total_devices == 0:
        #     return render_template('users.html', 
        #                          users=[],
        #                          has_devices=False,
        #                          translations=TRANSLATIONS,
        #                          current_language=session.get('dil', 'tr'))
        
        # Kullanıcıları getir - users.db'den çek
        conn = sqlite3.connect('users.db')
        conn.row_factory = sqlite3.Row
        
        # Liste kapsamı: admin tümünü görür; normal kullanıcı kendi kaydı + kendi cihazlarına atanmış kullanıcıları görür
        if is_admin:
            users_list = conn.execute('SELECT * FROM users ORDER BY created_at DESC').fetchall()
        else:
            # Non-admin: kendi ve yetkili olduğu cihazlardaki kullanıcılar (users.db + data.db üzerinden)
            device_ids = set()
            # 1) data.db cihaz sahipliği
            try:
                dtmp = get_db_connection(); dtmp.row_factory = sqlite3.Row
                for r in dtmp.execute('SELECT id FROM cihazlar WHERE user_id = ?', (user_id,)).fetchall():
                    device_ids.add(str(r['id']))
                dtmp.close()
            except Exception:
                pass
            # 2) users.db user_device_access: kullanıcının yetkili olduğu cihazlar
            try:
                for r in conn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall():
                    device_ids.add(str(r['device_id']))
            except Exception:
                pass
            # 3) data.db user_device_access: kullanıcının yetkili olduğu cihazlar
            try:
                dacc = get_db_connection(); dacc.row_factory = sqlite3.Row
                for r in dacc.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall():
                    device_ids.add(str(r['device_id']))
                dacc.close()
            except Exception:
                pass
            user_ids = set([int(user_id)])
            if device_ids:
                placeholders = ','.join(['?'] * len(device_ids))
                # users.db üzerindeki eşleşen kullanıcılar
                try:
                    rows = conn.execute(f"SELECT DISTINCT user_id FROM user_device_access WHERE device_id IN ({placeholders})", list(device_ids)).fetchall()
                    for r in rows:
                        try:
                            user_ids.add(int(r['user_id']))
                        except Exception:
                            pass
                except Exception:
                    pass
                # data.db üzerindeki eşleşen kullanıcılar
                try:
                    dacc2 = get_db_connection(); dacc2.row_factory = sqlite3.Row
                    rows2 = dacc2.execute(f"SELECT DISTINCT user_id FROM user_device_access WHERE device_id IN ({placeholders})", list(device_ids)).fetchall()
                    dacc2.close()
                    for r in rows2:
                        try:
                            user_ids.add(int(r['user_id']))
                        except Exception:
                            pass
                except Exception:
                    pass
            if user_ids:
                marks = ','.join(['?'] * len(user_ids))
                users_list = conn.execute(f"SELECT * FROM users WHERE id IN ({marks}) ORDER BY created_at DESC", list(user_ids)).fetchall()
            else:
                users_list = conn.execute('SELECT * FROM users WHERE id = ? ORDER BY created_at DESC', (user_id,)).fetchall()
        logger.info(f"Showing {len(users_list)} users total")
        # Kullanıcıların atanmış cihazlarını getir (user_device_access tablosu)
        user_devices_map = {}
        try:
            cursor = conn.cursor()
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS user_device_access (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    user_id INTEGER NOT NULL,
                    device_id TEXT NOT NULL
                )
            ''')
            cursor.execute('''
                CREATE UNIQUE INDEX IF NOT EXISTS idx_user_device_unique
                ON user_device_access(user_id, device_id)
            ''')
            user_ids = [u['id'] for u in users_list]
            if user_ids:
                placeholders = ','.join(['?' for _ in user_ids])
                rows = cursor.execute(f'''SELECT user_id, device_id FROM user_device_access 
                                          WHERE user_id IN ({placeholders})''', user_ids).fetchall()
                # Cihaz adlarını almak için data.db'den sorgula
                device_ids = sorted({r['device_id'] for r in rows})
                id_to_name = {}
                if device_ids:
                    data_conn = get_db_connection()
                    d_placeholders = ','.join(['?' for _ in device_ids])
                    drows = data_conn.execute(f'''SELECT id, ad FROM cihazlar WHERE id IN ({d_placeholders})''', device_ids).fetchall()
                    data_conn.close()
                    id_to_name = {str(r['id']): (r['ad'] or str(r['id'])) for r in drows}
                for r in rows:
                    uid = r['user_id']
                    did = str(r['device_id'])
                    if uid not in user_devices_map:
                        user_devices_map[uid] = []
                    user_devices_map[uid].append({'id': did, 'name': id_to_name.get(did, did)})
        except Exception as e:
            logger.error(f"Users route - user_device_access map error: {e}")
        
        conn.close()
        
        logger.info(f"Users route - returning {len(users_list)} users to template")
        for user in users_list:
            logger.info(f"User: {user['id']} - {user['full_name']}")
        
        # data.db'den profil resmi/şirket bilgisini öncelikli olarak al ve listeye uygula
        try:
            if users_list:
                data_conn = get_db_connection()
                ids = [str(u['id']) for u in users_list]
                placeholders = ','.join(['?'] * len(ids))
                rows = data_conn.execute(f"SELECT id, profile_image, company FROM users WHERE id IN ({placeholders})", ids).fetchall()
                data_conn.close()
                db_map = {str(r['id']): {'profile_image': r['profile_image'], 'company': r['company']} for r in rows}
                # users_list öğeleri Row; dict'e kopyalayacağız
                merged = []
                for u in users_list:
                    uid = str(u['id'])
                    row_dict = dict(u)
                    if uid in db_map:
                        if db_map[uid].get('profile_image'):
                            row_dict['profile_image'] = db_map[uid]['profile_image']
                        if db_map[uid].get('company') is not None:
                            row_dict['company'] = db_map[uid]['company']
                    merged.append(row_dict)
                users_list = merged
        except Exception as mge:
            logger.warning(f"users merge data.db profile/company warn: {mge}")

        # Server-side search (before pagination)
        try:
            q = (request.args.get('q') or '').strip().lower()
            if q:
                def _val(x):
                    return str(x or '')
                filtered = []
                for u in users_list:
                    hay = ' '.join([
                        _val(u.get('full_name') if isinstance(u, dict) else u['full_name']),
                        _val(u.get('email') if isinstance(u, dict) else u['email']),
                        _val(u.get('company') if isinstance(u, dict) else u['company'])
                    ]).lower()
                    if q in hay:
                        filtered.append(u)
                users_list = filtered
        except Exception:
            pass

        # Pagination: 50 per page
        try:
            per_page = 50
            try:
                page = int((request.args.get('page') or '1').strip())
            except Exception:
                page = 1
            if page < 1:
                page = 1
            total_users = len(users_list)
            total_pages = max(1, (total_users + per_page - 1) // per_page)
            if page > total_pages:
                page = total_pages
            start_idx = (page - 1) * per_page
            end_idx = start_idx + per_page
            users_list = users_list[start_idx:end_idx]
        except Exception as _pg:
            total_users = len(users_list)
            per_page = 50
            total_pages = 1
            page = 1

        # Navbar'da avatar için mevcut giriş yapan kullanıcıyı da şablona ilet
        current_user_row = None
        current_display_name = ''
        try:
            current_user_id = session.get('user_id')
            if current_user_id:
                # Önce data.db'den çek (profil resmi burada güncel)
                dconn = sqlite3.connect('data.db')
                dconn.row_factory = sqlite3.Row
                drow = dconn.execute('SELECT id, full_name, email, company, profile_image FROM users WHERE id = ?', (current_user_id,)).fetchone()
                dconn.close()
                if drow:
                    current_user_row = drow
                else:
                    # Fallback users.db
                    uconn = sqlite3.connect('users.db')
                    uconn.row_factory = sqlite3.Row
                    current_user_row = uconn.execute('SELECT id, full_name, email, company, profile_image FROM users WHERE id = ?', (current_user_id,)).fetchone()
                    uconn.close()
                if current_user_row:
                    current_display_name = current_user_row['full_name'] or current_user_row['email'] or ''
        except Exception as _:
            pass
        
        return render_template('users.html', 
                             users=users_list,
                             has_devices=(total_devices>0),
                             user_devices_map=user_devices_map,
                             user=current_user_row,
                             current_user=current_user_row,
                             user_name=current_display_name,
                             current_page=page,
                             total_pages=total_pages,
                             total_users=total_users,
                             per_page=per_page,
                             display_start=(0 if total_users==0 else ((page-1)*per_page)+1),
                             display_end=min(total_users, page*per_page),
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'),
                             static_bust=int(time.time()))

    # Kullanıcı ekleme route'u
    @app.route('/add-user', methods=['GET', 'POST'])
    def add_user():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        if request.method == 'POST':
            logger.info("POST request received for add_user")
            full_name = request.form.get('full_name', '').strip()
            company = request.form.get('company', '').strip()
            phone = request.form.get('phone', '').strip()
            user_type = request.form.get('user_type', 'standard')
            logger.info(f"Form data: full_name={full_name}, company={company}, phone={phone}, user_type={user_type}")
            
            # Çoklu erişim tipi desteği
            access_types = request.form.getlist('access_types')  # ['mobile', 'nfc'] gibi
            access_type = ','.join(access_types) if access_types else 'mobile'
            
            # UUID'lar
            card_uuid = request.form.get('card_uuid', '').strip() if 'nfc' in access_types else None
            user_uuid = request.form.get('user_uuid', '').strip() if 'mobile' in access_types else None
            # Mobil UUID doğrulama: sadece rakam ve en fazla 16 karakter
            if user_uuid:
                filtered_uuid = ''.join(ch for ch in user_uuid if ch.isdigit())[:16]
                if filtered_uuid != user_uuid:
                    flash('Mobil UUID yalnızca rakamlardan oluşmalı ve en fazla 16 hane olmalı', 'error')
                    return redirect(url_for('add_user'))
            
            # Mobil şifre (mobil seçiliyse zorunlu)
            mobile_password = request.form.get('mobile_password', '').strip() if 'mobile' in access_types else ''
            
            # Seçili cihazlar
            selected_devices = request.form.getlist('selected_devices')  # JavaScript'ten gelecek
            
            # Kullanıcı durumu (aktif/pasif) - varsayılan olarak aktif
            is_active = request.form.get('is_active') == 'on'
            
            # Zaman sınırlarını al
            start_date = request.form.get('start_date', '').strip()
            start_time = request.form.get('start_time', '').strip()
            end_date = request.form.get('end_date', '').strip()
            end_time = request.form.get('end_time', '').strip()
            enable_time_limits = request.form.get('enable_time_limits') == 'on'
            if enable_time_limits:
                from datetime import datetime
                today = datetime.now().strftime('%Y-%m-%d')
                if not start_date:
                    start_date = today
                if not start_time:
                    start_time = '09:00'
                if not end_date:
                    end_date = today
                if not end_time:
                    end_time = '18:00'
            
            logger.info(f"Date/time values: start_date={start_date}, start_time={start_time}, end_date={end_date}, end_time={end_time}")
            
            # Tarih ve saati birleştir
            time_limit_start = None
            time_limit_end = None
            
            if start_date and start_time:
                time_limit_start = f"{start_date}T{start_time}"
                logger.info(f"Combined start: {time_limit_start}")
            
            if end_date and end_time:
                time_limit_end = f"{end_date}T{end_time}"
                logger.info(f"Combined end: {time_limit_end}")
            
            # Profil resmi işleme
            profile_image_filename = None
            if 'profile_image' in request.files:
                file = request.files['profile_image']
                if file and file.filename:
                    allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
                    file_ext = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else ''
                    if file_ext in allowed_extensions:
                        import uuid
                        import os
                        unique_filename = f"{uuid.uuid4().hex}.{file_ext}"
                        upload_folder = os.path.join('static', 'uploads')
                        os.makedirs(upload_folder, exist_ok=True)
                        file_path = os.path.join(upload_folder, unique_filename)
                        file.save(file_path)
                        profile_image_filename = unique_filename
            
            try:
                logger.info(f"Adding user: {full_name}, profile_image: {profile_image_filename}")
                # Users database için doğru bağlantı
                conn = sqlite3.connect('users.db')
                conn.row_factory = sqlite3.Row
                
                # Kullanıcıyı ekle - email alanı opsiyonel; kullanıcı girdiyse kullan, yoksa NULL bırak
                form_email = (request.form.get('email') or '').strip()
                email = form_email if form_email else None
                # Mobil erişim için şifre zorunlu; hashle
                if 'mobile' in access_types:
                    if not mobile_password:
                        flash('Mobil erişim için şifre gereklidir', 'error')
                        conn.close()
                        return redirect(url_for('add_user'))
                    password_hash = generate_password_hash(mobile_password)
                else:
                    password_hash = "no_login_required"
                
                cursor = conn.execute('''
                    INSERT INTO users (email, password_hash, full_name, company, phone, user_type, access_type, 
                                     card_uuid, user_uuid, time_limit_start, time_limit_end, 
                                     profile_image, created_at, is_active) 
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
                ''', (email, password_hash, full_name, company, phone, user_type, access_type, 
                      card_uuid, user_uuid, time_limit_start, time_limit_end, profile_image_filename, is_active))
                
                user_id = cursor.lastrowid
                
                # Seçili cihazları kullanıcıya ata
                logger.info(f"User added with ID: {user_id}, selected devices: {selected_devices}")
                try:
                    # Eşleme tablosu oluştur
                    conn.execute('''
                        CREATE TABLE IF NOT EXISTS user_device_access (
                            id INTEGER PRIMARY KEY AUTOINCREMENT,
                            user_id INTEGER NOT NULL,
                            device_id TEXT NOT NULL
                        )
                    ''')
                    conn.execute('''
                        CREATE UNIQUE INDEX IF NOT EXISTS idx_user_device_unique
                        ON user_device_access(user_id, device_id)
                    ''')
                    # Atamaları ekle
                    if selected_devices:
                        for dev_id in selected_devices:
                            if dev_id:
                                conn.execute('INSERT OR IGNORE INTO user_device_access (user_id, device_id) VALUES (?, ?)', (user_id, str(dev_id)))
                except Exception as map_err:
                    logger.error(f"Device assignment error (add_user): {map_err}")
                
                conn.commit()
                conn.close()
                
                success = True
                message = f"Kullanıcı başarıyla eklendi ({len(selected_devices)} cihaza erişim verildi)"
                
                # NFC kartı varsa ve cihazlar atanmışsa ilgili cihazlara addcards ile gönder
                try:
                    if card_uuid and selected_devices:
                        # Her cihaz için hex'e dönüştürüp addcards gönder
                        def to_hex(u):
                            s = str(u or '').strip()
                            if not s:
                                return None
                            if s.lower().startswith('0x'):
                                return s
                            try:
                                n = int(s, 10)
                                return '0x' + format(n, 'x')
                            except Exception:
                                return None
                        hex_uuid = to_hex(card_uuid)
                        if hex_uuid:
                            for dev_id in selected_devices:
                                try:
                                    if tcp_manager.is_device_connected(str(dev_id)):
                                        # getdeviceinfo cache'ten isTerminal kontrolü
                                        is_terminal = False
                                        try:
                                            info = tcp_manager.get_device_info_from_cache(str(dev_id)) or {}
                                            data = info.get('data') or {}
                                            is_terminal = bool(data.get('isTerminal'))
                                        except Exception:
                                            is_terminal = False
                                        if not is_terminal:
                                            paused_prev = getattr(tcp_manager, 'pause_periodic_getdeviceinfo', False)
                                            tcp_manager.pause_periodic_getdeviceinfo = True
                                            try:
                                                payload = json.dumps({'addcards': [hex_uuid]})
                                                ok = tcp_manager.send_command_to_device(str(dev_id), payload)
                                                if ok:
                                                    tcp_manager.get_command_response(str(dev_id), timeout=10)
                                                    # Güncelleme gönder
                                                    try:
                                                        tcp_manager.send_command_to_device(str(dev_id), 'updatecards')
                                                        tcp_manager.get_command_response(str(dev_id), timeout=10)
                                                    except Exception:
                                                        pass
                                            finally:
                                                tcp_manager.pause_periodic_getdeviceinfo = paused_prev
                                except Exception:
                                    continue
                except Exception as _push_err:
                    logger.error(f"NFC kart push hata: {_push_err}")
                
            except Exception as e:
                logger.error(f"Add user error: {e}")
                import traceback
                logger.error(f"Add user traceback: {traceback.format_exc()}")
                success = False
                message = str(e)
            
            if success:
                flash('Kullanıcı başarıyla eklendi', 'success')
            else:
                flash(f'Kullanıcı ekleme hatası: {message}', 'error')
            
            return redirect(url_for('users'))
        
        # GET ise formu göster
        return render_template('add_user.html', 
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # Cihaz listesi API - Ana cihazlar tablosundan
    @app.route('/api/devices')
    def api_devices():
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
            
        try:
            user_id = session.get('user_id')
            is_admin = session.get('is_admin', False)
            company = session.get('company', '')
            
            # Ana cihazlar tablosundan al
            conn = sqlite3.connect('data.db')
            conn.row_factory = sqlite3.Row
            cursor = conn.cursor()
            
            if is_admin:
                # Admin ise tüm cihazları göster
                cursor.execute('SELECT * FROM cihazlar ORDER BY id DESC')
            else:
                # Normal kullanıcı ise sadece kendi eklediği cihazları göster
                cursor.execute('SELECT * FROM cihazlar WHERE user_id = ? ORDER BY id DESC', (user_id,))
            
            devices = cursor.fetchall()
            conn.close()
            
            device_list = []
            for device in devices:
                dev_id_str = str(device['id'])
                try:
                    connected = tcp_manager.is_device_connected(dev_id_str) if 'tcp_manager' in globals() else False
                except Exception:
                    connected = False
                # Normalize tcp_tipi to 'client' or 'server'
                raw_type = str(device['tcp_tipi']).strip().lower() if device['tcp_tipi'] is not None else ''
                if raw_type in ('client', 'tcpclient', 'tcp_client', 'tcp client'):
                    norm_type = 'client'
                elif raw_type in ('server', 'tcpserver', 'tcp_server', 'tcp server'):
                    norm_type = 'server'
                else:
                    norm_type = raw_type or 'server'
                device_list.append({
                    'id': device['id'],
                    'ad': device['ad'],
                    'seri_no': device['seri_no'],
                    'tcp_tipi': norm_type,
                    'ip_adresi': device['ip_adresi'],
                    'is_connected': connected
                })
            
            return jsonify({
                'success': True,
                'devices': device_list
            })
            
        except Exception as e:
            logger.error(f"Cihaz listesi API hatası: {e}")
            return jsonify({
                'success': False,
                'message': str(e)
            }), 500

    # Test sayfası
    @app.route('/test-api')
    def test_api():
        return '''
<!DOCTYPE html>
<html>
<head>
    <title>API Test</title>
</head>
<body>
    <h1>Devices API Test</h1>
    <button onclick="testAPI()">Test /api/devices</button>
    <div id="result"></div>

    <script>
        async function testAPI() {
            try {
                const response = await fetch('/api/devices');
                const data = await response.json();
                document.getElementById('result').innerHTML = 
                    '<h3>Status: ' + response.status + '</h3>' +
                    '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
            } catch (error) {
                document.getElementById('result').innerHTML = 
                    '<h3>Error</h3><p>' + error.message + '</p>';
            }
        }
    </script>
</body>
</html>
        '''

    # Cihaz ekleme route'u
    @app.route('/cihaz-ekle', methods=['GET', 'POST'])
    def cihaz_ekle():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        
        if request.method == 'POST':
            # Ortak form alanları
            device_name = request.form.get('device_name') or request.form.get('ad')
            serial_no = request.form.get('serial_number') or request.form.get('seri_no')
            tcp_tipi = request.form.get('device_type') or request.form.get('tcp_tipi')
            ip_adresi = request.form.get('ip_address') or request.form.get('ip_adresi', '')
            if tcp_tipi == 'server':
                ip_adresi = ''
            
            if is_admin:
                conn = get_db_connection()
                existing_device = conn.execute('SELECT id FROM cihazlar WHERE id = ? OR seri_no = ?', (serial_no, serial_no)).fetchone()
                if existing_device:
                    flash(_('device_serial_exists'), "error")
                    return render_template('device_form.html', 
                                         form_mode='add',
                                         device={'device_name': '', 'device_type': 'client', 'ip_address': '', 'serial_number': ''},
                                         translations=TRANSLATIONS,
                                         current_language=session.get('dil', 'tr'))
                
                conn.execute('INSERT INTO cihazlar (id, ad, seri_no, tcp_tipi, ip_adresi, user_id) VALUES (?, ?, ?, ?, ?, ?)', 
                            (serial_no, device_name, serial_no, tcp_tipi, ip_adresi, user_id))
                conn.commit()
                conn.close()
                flash(_('device_added_successfully'), "success")
                try:
                    # Karalisteden kaldır
                    tcp_manager.unblacklist(serial_no)
                    # TCP client ise IP varsa bağlantıyı başlat
                    if (tcp_tipi == 'client') and (ip_adresi and ip_adresi.strip() and ip_adresi != 'None'):
                        tcp_manager.connect_to_tcp_client_device(serial_no, ip_adresi, 80)
                except Exception:
                    pass
            else:
                # Normal kullanıcı da ana database'e ekler
                conn = get_db_connection()
                existing_device = conn.execute('SELECT id FROM cihazlar WHERE id = ? OR seri_no = ?', (serial_no, serial_no)).fetchone()
                if existing_device:
                    flash("Bu cihaz zaten mevcut", "error")
                    return render_template('device_form.html', 
                                         form_mode='add',
                                         device={'device_name': '', 'device_type': 'client', 'ip_address': '', 'serial_number': ''},
                                         translations=TRANSLATIONS,
                                         current_language=session.get('dil', 'tr'))
                
                conn.execute('INSERT INTO cihazlar (id, ad, seri_no, tcp_tipi, ip_adresi, user_id) VALUES (?, ?, ?, ?, ?, ?)', 
                            (serial_no, device_name, serial_no, tcp_tipi, ip_adresi, user_id))
                conn.commit()
                conn.close()
                flash("Cihaz başarıyla eklendi", "success")
                try:
                    tcp_manager.unblacklist(serial_no)
                    if (tcp_tipi == 'client') and (ip_adresi and ip_adresi.strip() and ip_adresi != 'None'):
                        tcp_manager.connect_to_tcp_client_device(serial_no, ip_adresi, 80)
                except Exception:
                    pass
            
            return redirect(url_for('devices'))
        
        # Ortak form şablonunu kullan
        return render_template('device_form.html',
                             form_mode='add',
                             device={'device_name': '', 'device_type': 'client', 'ip_address': '', 'serial_number': ''},
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # PDF indirme route'u
    @app.route('/download-pdf')
    def download_pdf():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        query = (request.args.get('adsoyad', '') or '').strip().lower()

        # 1) Veriyi hazırla (access_logs'tan, kullanıcı kapsamına göre)
        logs = []
        try:
            conn = get_db_connection()
            cur = conn.cursor()
            if is_admin:
                rows = cur.execute('''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                       FROM access_logs ORDER BY timestamp DESC LIMIT 1000''').fetchall()
            else:
                # Kullanıcıya atanmış cihazlar
                try:
                    assigned = user_manager.get_user_devices(user_id)
                    device_serials = [str(d['serial_number']) for d in assigned if ('serial_number' in d.keys() and d['serial_number'])]
                except Exception:
                    device_serials = []
                if device_serials:
                    placeholders = ','.join(['?'] * len(device_serials))
                    rows = cur.execute(f'''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                            FROM access_logs WHERE device_id IN ({placeholders})
                                            ORDER BY timestamp DESC LIMIT 1000''', device_serials).fetchall()
                else:
                    # Kapsam bulunamadıysa merkezi kayıtlardan düşmeyelim (PDF boş olmasın)
                    rows = cur.execute('''SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp
                                           FROM access_logs ORDER BY timestamp DESC LIMIT 1000''').fetchall()
            # Cihaz adları
            device_rows = cur.execute('SELECT id, ad, seri_no FROM cihazlar').fetchall()
            conn.close()
            device_name_map = {str(r[2]): r[1] for r in device_rows if r[1]}

            # UUID -> İsim haritası
            uuid_to_name = {}
            try:
                dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                for ur in dconn.execute('SELECT id, user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                    if ur['user_uuid']:
                        uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                dconn.close()
            except Exception:
                pass
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                for ur in uconn.execute('SELECT id, user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                    if ur['user_uuid'] and str(ur['user_uuid']) not in uuid_to_name:
                        uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                uconn.close()
            except Exception:
                pass

            # mobile_access_notes için hızlı erişim
            notes_by_access_id = {}
            try:
                mconn = sqlite3.connect('data.db'); mconn.row_factory = sqlite3.Row
                for nr in mconn.execute('SELECT access_log_id, note FROM mobile_access_notes'):
                    notes_by_access_id[str(nr['access_log_id'])] = nr['note']
                mconn.close()
            except Exception:
                pass

            # access_type etiketleri
            def access_label(at_value, is_term):
                try:
                    at_s = str(at_value)
                    if at_s == '1': return 'NFC'
                    if at_s == '2': return 'BUTTON'
                    if at_s == '3':
                        return _('mobile_page_label') if str(is_term) == '2' else 'TCP SERVER'
                    if at_s == '4': return 'TCP CLIENT'
                    if at_s == '5': return 'BLE'
                    if at_s == '6': return 'RS485'
                except Exception:
                    pass
                return 'UNKNOWN'

            # Normalize isim eşlemesi (hex/dec) ve Unknown yerine boş bırak
            def build_user_map_pdf():
                m = {}
                def add(uval, name):
                    s = str(uval or '').strip().lower()
                    if not s or s == 'mastercard':
                        return
                    try:
                        if s not in m: m[s] = name or ''
                        if s.startswith('0x'):
                            n=int(s,16); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x')
                            for k in (decs,hx,hx8):
                                if k not in m: m[k]=name or ''
                        elif s.isdigit():
                            n=int(s,10); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x')
                            for k in (decs,hx,hx8):
                                if k not in m: m[k]=name or ''
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', s):
                                n=int(s,16); decs=str(n); hx='0x'+format(n,'x'); hx8='0x'+format(n,'08x'); preserved='0x'+s
                                for k in (preserved,decs,hx,hx8):
                                    if k not in m: m[k]=name or ''
                    except Exception:
                        pass
                try:
                    d=sqlite3.connect('data.db'); d.row_factory=sqlite3.Row
                    for ur in d.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                        add(ur['card_uuid'], ur['full_name'])
                    d.close()
                except Exception:
                    pass
                try:
                    u=sqlite3.connect('users.db'); u.row_factory=sqlite3.Row
                    for ur in u.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                        add(ur['card_uuid'], ur['full_name'])
                    u.close()
                except Exception:
                    pass
                return m
            pdf_user_map = build_user_map_pdf()
            for r in rows:
                cu_key = str(r['card_uuid']).strip().lower()
                name = pdf_user_map.get(cu_key, '')
                if not name and cu_key:
                    try:
                        if cu_key.startswith('0x'):
                            n=int(cu_key,16); alt=[str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                        elif cu_key.isdigit():
                            n=int(cu_key,10); alt=[str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', cu_key):
                                n=int(cu_key,16); alt=['0x'+cu_key,'0x'+format(n,'x'),'0x'+format(n,'08x'),str(n)]
                            else:
                                alt=[]
                        for k in alt:
                            if k in pdf_user_map:
                                name = pdf_user_map[k]; break
                    except Exception:
                        pass
                item = {
                    'id': r['id'],
                    'device_id': str(r['device_id']),
                    'user_name': name,
                    'device_name': device_name_map.get(str(r['device_id']), ''),
                    'card_uuid': r['card_uuid'],
                    'access_type': (
                        _('web_page_label') if (str(r['access_type'])=='3' and str(r['is_terminal'])=='2' and notes_by_access_id.get(str(r['id']))=='Admin Panel') else (
                            _('mobile_page_label') if (str(r['access_type'])=='3' and str(r['is_terminal'])=='2') else access_label(r['access_type'], r['is_terminal'])
                        )
                    ),
                    'status': (_('IZIN_VERILDI') if r['is_granted'] else _('IZIN_VERILMEDI')),
                    'notes': (notes_by_access_id.get(str(r['id']), _('device_data')) if str(r['is_terminal']) == '2' else _('device_data')),
                    'timestamp': r['timestamp']
                }
                logs.append(item)
        except Exception as e:
            logger.error(f"download_pdf veri hazırlama hatası: {e}")
            logs = []

        # 2) Filtre uygula (opsiyonel)
        if query:
            filtered = []
            for g in logs:
                haystack = ' '.join([
                    str(g.get('user_name','')),
                    str(g.get('card_uuid','')),
                    str(g.get('device_name','')),
                    str(g.get('device_id','')),
                    str(g.get('notes','')),
                    str(g.get('access_type','')),
                    str(g.get('status','')),
                    str(g.get('timestamp',''))
                ]).lower()
                if query in haystack:
                    filtered.append(g)
            if filtered:
                logs = filtered

        # 3) PDF oluştur
        pdf = FPDF('L', 'mm', 'A4')
        pdf.set_auto_page_break(auto=True, margin=10)
        pdf.set_margins(10, 10, 10)
        pdf.add_font('DejaVu', '', 'static/fonts/DejaVuSans.ttf', uni=True)
        pdf.add_page()

        # Başlık
        pdf.set_font('DejaVu', '', 14)
        pdf.cell(0, 10, _("access_logs_title"), ln=1, align='C')

        # Sütunlar ve oranlar (toplam 100%)
        headers = [_("user_name"), _("device_name"), "UUID", _("access_type"), _("status"), _("description"), _("time")]
        col_perc = [16, 16, 20, 9, 9, 16, 14]  # toplam 100
        avail_w = pdf.w - pdf.l_margin - pdf.r_margin
        col_widths = [avail_w * p / 100.0 for p in col_perc]

        # Header stili
        pdf.set_font('DejaVu', '', 9)
        pdf.set_fill_color(230, 240, 255)
        pdf.set_text_color(0, 0, 0)
        header_h = 8
        for i, h in enumerate(headers):
            pdf.cell(col_widths[i], header_h, h, border=1, align='C', fill=True)
        pdf.ln()

        # Yardımcı: metni hücre genişliğine sığdır (kırp + ...)
        def write_row(values):
            row_h = 7
            # Sayfa taşmasını kontrol et, yeni sayfada başlık tekrarla
            if pdf.get_y() + row_h > pdf.page_break_trigger:
                pdf.add_page()
                pdf.set_font('DejaVu', '', 9)
                pdf.set_fill_color(230, 240, 255)
                for i, h in enumerate(headers):
                    pdf.cell(col_widths[i], header_h, h, border=1, align='C', fill=True)
                pdf.ln()
            pdf.set_font('DejaVu', '', 8)
            for i, (txt, w) in enumerate(zip(values, col_widths)):
                s = str(txt or '')
                # Kırpma: yaklaşık karakter genişliği hesapla
                max_w = w - 2  # küçük tampon
                # Metni sığdır
                if pdf.get_string_width(s) > max_w:
                    # Bitişten kırp ve '...'
                    while len(s) > 0 and pdf.get_string_width(s + '...') > max_w:
                        s = s[:-1]
                    s = (s + '...') if s else ''
                pdf.cell(w, row_h, s, border=1, align='L')
            pdf.ln()

        # Notları i18n'e normalize et (TR/EN karışık gelebilir)
        def normalize_note(note_value: str) -> str:
            n = (str(note_value or '')).strip().lower()
            if n in ['mesai giriş','mesai giris','shift in']:
                return _('shift_in_label')
            if n in ['mesai çıkış','mesai cikis','shift out']:
                return _('shift_out_label')
            if n in ['mola giriş','mola giris','break in']:
                return _('break_in_label')
            if n in ['mola çıkış','mola cikis','break out']:
                return _('break_out_label')
            if n in ['misafir giriş','misafir giris','guest in']:
                return _('guest_in_label')
            if n in ['misafir çıkış','misafir cikis','guest out']:
                return _('guest_out_label')
            if n in ['mobil uygulama giriş','mobil uygulama giris','mobile app entry']:
                return _('mobile_app_entry')
            if n in ['cihaz verisi','device data']:
                return _('device_data')
            if n in ['admin panel']:
                return _('admin_panel')
            return note_value or ''

        # Satırları yaz (maksimum 2000 satır)
        for row in logs[:2000]:
            values = [
                row.get('user_name',''),
                (row.get('device_name') or row.get('device_id','')),
                row.get('card_uuid',''),
                row.get('access_type',''),
                row.get('status',''),
                normalize_note(row.get('notes','')),
                row.get('timestamp','')
            ]
            write_row(values)

        # Çıktı hazırla
        try:
            content = pdf.output(dest='S').encode('latin-1', errors='ignore')
            pdf_buffer = BytesIO(content)
        except Exception:
            pdf_buffer = BytesIO()
            pdf.output(pdf_buffer)
            pdf_buffer.seek(0)
        filename = f"gecis_kayitlari_{time.strftime('%Y%m%d_%H%M%S')}.pdf"
        return send_file(pdf_buffer, as_attachment=True, download_name=filename, mimetype='application/pdf')

    # Device info route'u
    @app.route('/deviceinfo')
    def device_info():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        
        logger.info(f"Deviceinfo route çağrıldı - User ID: {user_id}, Admin: {is_admin}")
        
        try:
            if is_admin:
                # Admin için tüm cihaz bilgilerini göster
                conn = get_db_connection()
                device_infos = conn.execute('''
                    SELECT di.*, c.ad as device_name 
                    FROM deviceinfo di
                    LEFT JOIN cihazlar c ON di.device_id = c.id
                    ORDER BY di.last_update DESC 
                    LIMIT 100
                ''').fetchall()
                conn.close()
                logger.info(f"Admin için {len(device_infos)} cihaz bilgisi bulundu")
            else:
                # Normal kullanıcı için sadece ilk cihazını göster
                from user_management import UserManager
                user_manager = UserManager()
                user_devices = user_manager.get_user_devices(user_id)
                logger.info(f"Kullanıcı {user_id} için {len(user_devices)} cihaz bulundu")
                
                # Sadece ilk cihazı al
                device_infos = []
                if user_devices:
                    user_device = user_devices[0]  # İlk cihazı al
                    device_id = str(user_device['serial_number'])
                    
                    # deviceinfo tablosundan firmware ve IP bilgisini al
                    firmware_version = 'N/A'
                    ip_address = user_device['ip_address']  # Varsayılan olarak user_device'dan al
                    try:
                        conn = get_db_connection()
                        device_info = conn.execute('''
                            SELECT firmware_version, ip_address FROM deviceinfo 
                            WHERE device_id = ?
                        ''', (device_id,)).fetchone()
                        conn.close()
                        
                        if device_info:
                            if device_info['firmware_version']:
                                firmware_version = device_info['firmware_version']
                            if device_info['ip_address']:
                                ip_address = device_info['ip_address']
                    except Exception as e:
                        logger.error(f"Device info alma hatası: {e}")
                    
                    device_info = {
                        'id': user_device['id'],
                        'device_id': user_device['serial_number'],
                        'ip_address': ip_address,
                        'device_name': user_device['device_name'],
                        'firmware_version': firmware_version,
                        'last_update': user_device['created_at'],
                        'status': 'online' if tcp_manager.is_device_connected(device_id) else 'offline'
                    }
                    device_infos.append(device_info)
                
                logger.info(f"Kullanıcının ilk cihazı deviceinfo formatına çevrildi")
            
            logger.info(f"Toplam {len(device_infos)} cihaz bilgisi döndürülüyor")
            
        except Exception as e:
            logger.error(f"Device info getirme hatası: {e}")
            device_infos = []
        
        return render_template('deviceinfo.html', 
                             device_infos=device_infos,
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # JSON logs route'u
    @app.route('/json-logs')
    def json_logs():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        
        conn = get_db_connection()
        
        if is_admin:
            json_logs = conn.execute('''
                SELECT * FROM json_data_logs 
                ORDER BY received_at DESC 
                LIMIT 100
            ''').fetchall()
        else:
            # Normal kullanıcı için user_management'den cihaz bilgilerini al
            try:
                from user_management import UserManager
                user_manager = UserManager()
                user_devices = user_manager.get_user_devices(user_id)
                device_serial_numbers = [str(device['serial_number']) for device in user_devices]
                
                if device_serial_numbers:
                    placeholders = ','.join(['?' for _ in device_serial_numbers])
                    json_logs = conn.execute(f'''
                        SELECT * FROM json_data_logs 
                        WHERE source_ip IN (
                            SELECT DISTINCT ip_address FROM deviceinfo 
                            WHERE device_id IN ({placeholders})
                        )
                        ORDER BY received_at DESC 
                        LIMIT 100
                    ''', device_serial_numbers).fetchall()
                else:
                    json_logs = []
            except Exception as e:
                logger.error(f"JSON logs getirme hatası: {e}")
                json_logs = []
        
        conn.close()
        
        json_logs = []  # JSON log tutma kaldırıldı
        return render_template('json_logs.html', 
                             json_logs=json_logs,
                             user_name=session.get('user_name'),
                             is_admin=is_admin,
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # Kullanıcı cihaz düzenleme route'u
    @app.route('/device/edit/user/<device_id>', methods=['GET', 'POST'])
    def edit_user_device(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        
        # Ana database'den cihazı getir
        conn = get_db_connection()
        device = conn.execute('SELECT * FROM cihazlar WHERE id = ? AND user_id = ?', (device_id, user_id)).fetchone()
        conn.close()
        
        if not device:
            flash('Cihaz bulunamadı veya size ait değil!', 'error')
            return redirect(url_for('devices'))
        
        if request.method == 'POST':
            device_name = request.form.get('device_name', '')
            device_type = request.form.get('device_type', '')
            ip_address = request.form.get('ip_address', '')
            serial_number = request.form.get('serial_number', '')
            
            if not device_name or not device_type:
                flash(_('device_update_error') + ': ' + _('required_fields_missing'), 'error')
                return redirect(url_for('edit_user_device', device_id=device_id))
            
            try:
                conn = get_db_connection()
                # TCP Server ise ip boşalt
                save_ip = '' if (device_type == 'server') else ip_address
                conn.execute('''
                    UPDATE cihazlar 
                    SET ad = ?, tcp_tipi = ?, ip_adresi = ?, seri_no = ?
                    WHERE id = ? AND user_id = ?
                ''', (device_name, device_type, save_ip, serial_number, device_id, user_id))
                conn.commit()
                conn.close()
                
                flash('Cihaz başarıyla güncellendi', 'success')
                try:
                    tcp_manager.unblacklist(serial_number or device_id)
                    if (device_type == 'client') and (ip_address and ip_address.strip() and ip_address != 'None'):
                        tcp_manager.connect_to_tcp_client_device(serial_number or device_id, ip_address, 80)
                except Exception:
                    pass
                return redirect(url_for('devices'))
            except Exception as e:
                flash(f'Cihaz güncelleme hatası: {e}', 'error')
                return redirect(url_for('edit_user_device', device_id=device_id))
        
        is_connected = tcp_manager.is_device_connected(str(device_id))
        
        # Template için device objesini uyumlu formata çevir
        device_data = {
            'device_name': device['ad'],
            'device_type': device['tcp_tipi'],
            'ip_address': device['ip_adresi'],
            'serial_number': device['seri_no'],
            'id': device['id']
        }
        
        return render_template('device_form.html', 
                             form_mode='edit',
                             device=device_data,
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))

    # Kullanıcı cihaz silme route'u - YENİ TEK DATABASE SİSTEMİ
    @app.route('/device/delete/user/<device_id>')
    def delete_user_device(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        
        try:
            # Ana database'den cihazı kontrol et ve sil
            conn = get_db_connection()
            device = conn.execute('SELECT * FROM cihazlar WHERE id = ? AND user_id = ?', (device_id, user_id)).fetchone()
            
            if not device:
                flash('Cihaz bulunamadı veya size ait değil!', 'error')
            else:
                conn.execute('DELETE FROM cihazlar WHERE id = ? AND user_id = ?', (device_id, user_id))
                conn.commit()
                flash('Cihaz başarıyla silindi', 'success')
                
            conn.close()
                
        except Exception as e:
            logger.error(f"Cihaz silme hatası: {e}")
            flash(f'Cihaz silinirken hata oluştu: {e}', 'error')
        
        return redirect(url_for('devices'))

    # Ana cihaz silme route'u
    @app.route('/device/delete/<device_id>')
    def delete_device(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        is_admin = session.get('is_admin', False)
        if not is_admin:
            flash('Bu işlem için admin yetkisi gereklidir.', 'error')
            return redirect(url_for('devices'))
        
        try:
            # Ana cihazlar tablosundan sil
            conn = get_db_connection()
            conn.execute('DELETE FROM cihazlar WHERE id = ?', (device_id,))
            conn.commit()
            conn.close()
            
            # TCP bağlantısını kapat
            if tcp_manager.is_device_connected(str(device_id)):
                tcp_manager.disconnect_device(str(device_id))
            
            flash('Cihaz başarıyla silindi.', 'success')
                
        except Exception as e:
            logger.error(f"Cihaz silme hatası: {e}")
            flash(f'Cihaz silme hatası: {e}', 'error')
        
        return redirect(url_for('devices'))

    # Debug TCP status route'u
    # Cache temizleme API
    @app.route('/api/clear-cache', methods=['POST'])
    def clear_cache():
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        
        try:
            tcp_manager.clear_device_cache()
            return jsonify({'success': True, 'message': 'Cache cleared'})
        except Exception as e:
            logger.error(f"Cache temizleme hatası: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/debug/tcp-status')
    def debug_tcp_status():
        """TCP Manager durumunu debug için gösterir"""
        if not session.get('logged_in'):
            return jsonify({'error': 'Unauthorized'}), 401
        
        try:
            return jsonify({
                'client_connections': list(tcp_manager.client_connections.keys()),
                'connected_device_ips': dict(tcp_manager.connected_device_ips),
                'device_response_status': {
                    k: {
                        'last_response': v.get('last_response'),
                        'failed_attempts': v.get('failed_attempts'),
                        'last_ping_time': v.get('last_ping_time')
                    } for k, v in tcp_manager.device_response_status.items()
                },
                'running': tcp_manager.running,
                'server_socket': tcp_manager.server_socket is not None
            })
        except Exception as e:
            logger.error(f"Debug TCP status hatası: {e}")
            return jsonify({'error': str(e)}), 500

    # Cihaz bağlantı durumu route'u
    @app.route('/api/device/status/<device_id>')
    def device_connection_status(device_id):
        """Belirli bir cihazın bağlantı durumunu kontrol eder"""
        if not session.get('logged_in'):
            return jsonify({'error': 'Unauthorized'}), 401
        
        try:
            is_connected = tcp_manager.is_device_connected(device_id)
            device_id_str = str(device_id)
            
            # Cihazın son yanıt zamanını al
            last_response = None
            if device_id_str in tcp_manager.device_response_status:
                last_response = tcp_manager.device_response_status[device_id_str].get('last_response')
            
            return jsonify({
                'device_id': device_id,
                'connected': is_connected,
                'last_response': last_response,
                'in_client_connections': device_id_str in tcp_manager.client_connections,
                'in_response_status': device_id_str in tcp_manager.device_response_status
            })
        except Exception as e:
            logger.error(f"Cihaz durumu kontrol hatası: {e}")
            return jsonify({'error': str(e)}), 500

    # Cihaz durumu route'u
    @app.route('/api/device/status')
    def device_status():
        status_list = []
        
        conn = get_db_connection()
        devices = conn.execute('SELECT * FROM cihazlar').fetchall()
        conn.close()
        
        for device in devices:
            device_id_str = str(device['id'])
            is_connected = tcp_manager.is_device_connected(device_id_str)
            status_list.append({
                'id': device_id_str,
                'name': device['ad'],
                'tcp_type': device['tcp_tipi'],
                'ip_address': device['ip_adresi'],
                'connected': is_connected
            })
        
        try:
            from user_management import UserManager
            user_manager = UserManager()
            user_devices = user_manager.get_all_user_devices()
            for user_device in user_devices:
                device_id_str = str(user_device['serial_number'])
                
                existing_device = None
                for existing in status_list:
                    if existing['id'] == device_id_str:
                        existing_device = existing
                        break
                
                if existing_device:
                    is_connected = tcp_manager.is_device_connected(device_id_str)
                    existing_device['connected'] = is_connected
                else:
                    is_connected = tcp_manager.is_device_connected(device_id_str)
                    status_list.append({
                        'id': device_id_str,
                        'name': f"{user_device['device_name']} (User: {user_device['user_id']})",
                        'tcp_type': user_device['device_type'],
                        'ip_address': user_device['ip_address'],
                        'connected': is_connected
                    })
        except Exception as e:
            logger.error(f"Kullanıcı cihazları getirme hatası: {e}")
        
        return {'devices': status_list}

    # Cihaz logları route'u  
    @app.route('/device-logs/<device_id>')
    def device_logs(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        user_name = session.get('user_name') or ''
        
        # Navbar için user objesi
        user_info = None
        try:
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                urow = uconn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                uconn.close()
                if urow:
                    user_info = urow
            except Exception:
                user_info = None
            if not user_info:
                conn = get_db_connection()
                user_info = conn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                conn.close()
        except Exception:
            user_info = None
        
        # Sayfalama parametreleri
        page = int(request.args.get('page', 1))
        per_page = 20
        search_q = (request.args.get('adsoyad', '') or '').strip().lower()
        filter_type = (request.args.get('type', '') or '').strip().upper()
        start_date_str = (request.args.get('start_date', '') or '').strip()
        end_date_str = (request.args.get('end_date', '') or '').strip()
        
        # Debug modu (isteğe bağlı)
        debug_mode = (request.args.get('debug', '') == '1')
        if debug_mode:
            try:
                logger.info(f"[device-logs][debug] params device_id={device_id} page={page} filter_type={filter_type} search='{search_q}' start='{start_date_str}' end='{end_date_str}'")
            except Exception:
                pass
        
        # Cihaz bilgisini al
        device = None
        if is_admin:
            conn = get_db_connection()
            device_data = conn.execute('SELECT * FROM cihazlar WHERE id = ? OR seri_no = ?', (device_id, device_id)).fetchone()
            conn.close()
            if device_data:
                device = {'name': device_data['ad'], 'id': device_data['id']}
        
        if not device:
            # User cihazlarından kontrol et
            user_devices = user_manager.get_user_devices(user_id)
            for user_device in user_devices:
                if str(user_device['serial_number']) == str(device_id):
                    device = {'name': user_device['device_name'], 'id': device_id}
                    break
        
        if not device:
            # Lokal bağımsız: sadece ID göster
            device = {'name': str(device_id), 'id': device_id}
        
        # Cihaz loglarını al (cihazdan readlog çek)
        logs = []
        total_logs = 0
        parsed_entries = []
        try:
            # Cihaz bağlıysa readlog çek
            if tcp_manager.is_device_connected(str(device_id)):
                ok = tcp_manager.send_command_to_device(str(device_id), 'readlog')
                if ok:
                    resp = tcp_manager.get_command_response(str(device_id), timeout=30)
                    if resp and resp.get('status') == 'success':
                        raw = resp.get('data', b'')
                        try:
                            text = raw.decode('utf-8', errors='ignore') if isinstance(raw, (bytes, bytearray)) else str(raw)
                        except Exception:
                            text = ''
                        # JSON bloklarını ayrıştır (tcp_manager._iter_json_stream)
                        for obj in tcp_manager._iter_json_stream(text):
                            try:
                                # Liste kök gelirse ve öğeler event ise
                                if isinstance(obj, list):
                                    for it in obj:
                                        if isinstance(it, dict) and any(k in it for k in ['deviceid','uuid','type','isterminal','isgranted','timestamp','deviceId','UUID','Type']):
                                            parsed_entries.append(it)
                                    continue
                                if not isinstance(obj, dict):
                                    continue
                                items = None
                                if 'accessLog' in obj:
                                    items = obj.get('accessLog')
                                elif 'AccessLog' in obj:
                                    items = obj.get('AccessLog')
                                elif 'accesslog' in obj:
                                    items = obj.get('accesslog')
                                elif isinstance(obj.get('data'), dict):
                                    d = obj.get('data')
                                    items = d.get('accessLog') or d.get('AccessLog') or d.get('accesslog')
                                # Tekil event objesi ise doğrudan ekle
                                elif any(k in obj for k in ['deviceid','uuid','type','isterminal','isgranted','timestamp','deviceId','UUID','Type']):
                                    items = [obj]
                                if isinstance(items, list):
                                    parsed_entries.extend([e for e in items if isinstance(e, dict)])
                            except Exception:
                                continue
                        # JSON çıkmadıysa binary struct fallback (40 byte kayıtlar)
                        try:
                            if (not parsed_entries) and isinstance(raw, (bytes, bytearray)) and len(raw) >= 40:
                                buf = bytes(raw)
                                rec_size = 40
                                for i in range(0, len(buf) - rec_size + 1, rec_size):
                                    chunk = buf[i:i+rec_size]
                                    access_type_val = chunk[0]
                                    is_granted_val = chunk[1]
                                    ts_bytes = chunk[2:27]
                                    uuid_bytes = chunk[27:39]
                                    term_mode_val = chunk[39]
                                    # Clean strings
                                    def clean(bs):
                                        try:
                                            return bs.split(b'\x00', 1)[0].decode('utf-8', errors='ignore').strip().strip('\x00')
                                        except Exception:
                                            return ''
                                    ts_str = clean(ts_bytes)
                                    uuid_str = clean(uuid_bytes)
                                    # Map type number to label
                                    type_map = {1:'NFC', 2:'BUTTON', 3:'TCP SERVER', 4:'TCP CLIENT', 5:'BLE', 6:'RS485'}
                                    type_label = type_map.get(int(access_type_val), 'UNKNOWN')
                                    parsed_entries.append({
                                        'deviceid': device_id,
                                        'type': type_label,
                                        'uuid': uuid_str,
                                        'isterminal': True if int(term_mode_val) == 1 else False,
                                        'isgranted': True if int(is_granted_val) == 222 else False,
                                        'timestamp': ts_str
                                    })
                        except Exception:
                            pass
        except Exception as e:
            logger.error(f"readlog parse hata: {e}")
        # parsed_entries'i tablo formatına çevir
        try:
            if parsed_entries:
                # Konsola JSON satırları olarak yaz
                try:
                    import json as _json
                    for e in parsed_entries:
                        try:
                            logger.info("DEVICE_LOG_ENTRY_JSON: " + _json.dumps(e, ensure_ascii=False))
                        except Exception:
                            pass
                except Exception:
                    pass
                if debug_mode:
                    try:
                        sample = [{
                            'uuid': (e.get('uuid') or e.get('UUID') or ''),
                            'type': (e.get('type') or e.get('Type') or ''),
                            'timestamp': (e.get('timestamp') or e.get('time') or '')
                        } for e in parsed_entries[:10]]
                        logger.info(f"[device-logs][debug] parsed_entries count={len(parsed_entries)} sample={sample}")
                    except Exception:
                        pass
                # Cihaz adı lookup
                dev_name = None
                try:
                    conn = get_db_connection()
                    rr = conn.execute('SELECT ad FROM cihazlar WHERE id = ? OR seri_no = ?', (device_id, device_id)).fetchone()
                    conn.close()
                    if rr and rr['ad']:
                        dev_name = rr['ad']
                except Exception:
                    dev_name = None
                mapped = []
                for e in parsed_entries:
                    atype_str = (str((e.get('type') or e.get('Type') or '')).upper())
                    atype_map = {'NFC':1, 'BUTTON':2, 'TCP SERVER':3, 'TCP CLIENT':4, 'BLE':5, 'RS485':6}
                    isg = e.get('isgranted')
                    if isinstance(isg, str):
                         isg = True if isg.lower() in ['222','true','yes'] else False
                    # Kullanıcı adı çözümle (UUID->full_name) - normalize hex/dec
                    uname = ''
                    name_source = 'none'
                    cu = (e.get('uuid') or e.get('UUID') or '').strip()
                    if cu:
                        key = cu.strip().lower()
                        candidates = [key]
                        try:
                            if key.startswith('0x'):
                                n=int(key,16); candidates += [str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                            elif key.isdigit():
                                n=int(key,10); candidates += [str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                            else:
                                import re
                                if re.fullmatch(r'[0-9a-f]+', key):
                                    n=int(key,16); candidates += ['0x'+key,'0x'+format(n,'x'),'0x'+format(n,'08x'),str(n)]
                        except Exception:
                            pass
                        # users.db card_uuid
                        try:
                            u1 = sqlite3.connect('users.db'); u1.row_factory = sqlite3.Row
                            for k in candidates:
                                ur = u1.execute('SELECT full_name FROM users WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                            if ur and ur['full_name']:
                                uname = ur['full_name']
                                name_source = 'users.db.users.card_uuid'
                                break
                            u1.close()
                        except Exception:
                            pass
                        # data.db card_uuid
                        if not uname:
                            try:
                                d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                                for k in candidates:
                                    dr = d1.execute('SELECT full_name FROM users WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                                if dr and dr['full_name']:
                                    uname = dr['full_name']
                                    name_source = 'data.db.users.card_uuid'
                                    break
                                d1.close()
                            except Exception:
                                pass
                            # device_cards.user_name fallback
                        if not uname:
                            try:
                                d2 = sqlite3.connect('data.db'); d2.row_factory = sqlite3.Row
                                for k in candidates:
                                    rr = d2.execute('SELECT user_name FROM device_cards WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                                if rr and rr['user_name']:
                                    uname = rr['user_name']
                                    name_source = 'data.db.device_cards.card_uuid'
                                    break
                                d2.close()
                            except Exception:
                                pass
                    if debug_mode:
                        try:
                            norm1 = cu.replace('-', '').strip()
                            norm2 = norm1.upper()
                            logger.info(f"[device-logs][debug] uuid_map uuid='{cu}' norm='{norm1}' norm_upper='{norm2}' name='{uname}' source='{name_source}' type='{atype_str}' ts='{e.get('timestamp') or e.get('time') or ''}' device='{device_id}'")
                        except Exception:
                            pass
                    mapped.append({
                        'device_id': (e.get('deviceid') or e.get('deviceId') or device_id),
                        'user_name': uname,
                        'device_name': dev_name or (e.get('deviceid') or e.get('deviceId') or ''),
                        'card_uuid': e.get('uuid') or e.get('UUID') or '',
                        'access_type': atype_map.get(atype_str, None),
                        'type_label': (e.get('type') or e.get('Type') or atype_str),
                        'is_granted': True if (isg in (1, True) or isg == 222) else False,
                        'timestamp': e.get('timestamp') or e.get('time') or ''
                    })
                if debug_mode:
                    try:
                        unresolved_all = list({str(i.get('card_uuid')).strip() for i in mapped if (i.get('card_uuid') and not i.get('user_name'))})
                        logger.info(f"[device-logs][debug] unresolved after mapping count={len(unresolved_all)} sample={unresolved_all[:5] if unresolved_all else []}")
                    except Exception:
                        pass
                # Filtre uygula
                if search_q:
                    mapped = [m for m in mapped if (search_q in str(m.get('user_name') or '').lower()) or (search_q in str(m.get('device_name') or '').lower()) or (search_q in str(m.get('card_uuid') or '').lower()) or (search_q in str(m.get('device_id') or '').lower())]
                if filter_type:
                    mapped = [m for m in mapped if str(m.get('type_label') or '').upper() == filter_type]
                # Tarih aralığı filtresi (YYYY-MM-DD)
                date_filter_active = bool(start_date_str or end_date_str)
                if date_filter_active:
                    from datetime import datetime as _dt
                    sdate = None; edate = None
                    try:
                        if start_date_str:
                            sdate = _dt.strptime(start_date_str[:10], '%Y-%m-%d').date()
                    except Exception:
                        sdate = None
                    try:
                        if end_date_str:
                            edate = _dt.strptime(end_date_str[:10], '%Y-%m-%d').date()
                    except Exception:
                        edate = None
                    def in_range(ts):
                        try:
                            d = _dt.strptime(str(ts), '%d-%m-%y %H:%M:%S').date()
                        except Exception:
                            return True
                        if sdate and d < sdate:
                            return False
                        if edate and d > edate:
                            return False
                        return True
                    mapped = [m for m in mapped if in_range(m.get('timestamp'))]
                # Access logs-style bulk UUID->name resolution using users.user_uuid or users.card_uuid (run once regardless of date filter)
                try:
                    unresolved = list({str(i.get('card_uuid')).strip() for i in mapped if (i.get('card_uuid') and not i.get('user_name'))})
                    logger.info(f"[device-logs] unresolved uuids count={len(unresolved)} sample={unresolved[:5] if unresolved else []}")
                    if unresolved:
                        name_map = {}
                        q_marks = ','.join(['?'] * len(unresolved))
                        try:
                            u1 = sqlite3.connect('users.db'); u1.row_factory = sqlite3.Row
                            for ur in u1.execute(f'SELECT user_uuid, card_uuid, full_name FROM users WHERE user_uuid IN ({q_marks}) OR card_uuid IN ({q_marks})', unresolved + unresolved):
                                cu1 = str(ur['user_uuid'] or '').strip()
                                cu2 = str(ur['card_uuid'] or '').strip()
                                fn = ur['full_name']
                                if fn:
                                    if cu1:
                                        name_map[cu1] = fn
                                    if cu2:
                                        name_map[cu2] = fn
                            u1.close()
                            logger.info(f"[device-logs] users.db resolved names count={len(name_map)}")
                        except Exception as e:
                            logger.error(f"[device-logs] users.db uuid->name error: {e}")
                        try:
                            d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                            for ur in d1.execute(f'SELECT user_uuid, card_uuid, full_name FROM users WHERE user_uuid IN ({q_marks}) OR card_uuid IN ({q_marks})', unresolved + unresolved):
                                cu1 = str(ur['user_uuid'] or '').strip()
                                cu2 = str(ur['card_uuid'] or '').strip()
                                fn = ur['full_name']
                                if fn:
                                    if cu1 and cu1 not in name_map:
                                        name_map[cu1] = fn
                                    if cu2 and cu2 not in name_map:
                                        name_map[cu2] = fn
                            d1.close()
                            logger.info(f"[device-logs] data.db resolved (add) names count={len(name_map)}")
                        except Exception as e:
                            logger.error(f"[device-logs] data.db uuid->name error: {e}")
                        for it in mapped:
                            if it.get('card_uuid') and not it.get('user_name'):
                                key = str(it['card_uuid']).strip()
                                if key in name_map:
                                    it['user_name'] = name_map[key]
                    still = [str(i.get('card_uuid')) for i in mapped if (i.get('card_uuid') and not i.get('user_name'))]
                    if still:
                        logger.info(f"[device-logs] still unresolved count={len(still)} sample={still[:5]}")
                except Exception as e:
                    logger.error(f"[device-logs] bulk uuid->name resolution error: {e}")
                # Debug: unresolved UUIDs even after filters
                try:
                    unresolved2 = list({str(i.get('card_uuid')).strip() for i in mapped if (i.get('card_uuid') and not i.get('user_name'))})
                    if unresolved2:
                        logger.info(f"[device-logs] post-filter unresolved count={len(unresolved2)} sample={unresolved2[:5]}")
                    else:
                        logger.info("[device-logs] all names resolved post-filter")
                except Exception as e:
                    logger.error(f"[device-logs] post-filter debug error: {e}")
                # Boş isimleri yerelleştirilmiş 'unknown_user' ile doldur
                for it in mapped:
                    if not (it.get('user_name') or '').strip():
                        it['user_name'] = _('unknown_user')
                # Sırala yeni->eski (timestamp stringe göre)
                mapped.sort(key=lambda x: str(x.get('timestamp','')), reverse=True)
                total_logs = len(mapped)
                start_idx = (page - 1) * per_page
                end_idx = start_idx + per_page
                logs = mapped[start_idx:end_idx]
                for i, log in enumerate(logs):
                    log['display_index'] = total_logs - start_idx - i
            else:
                # Fallback: hafızadaki tcp_manager.device_logs kullan
                try:
                    if hasattr(tcp_manager, 'device_logs') and device_id in tcp_manager.device_logs:
                        all_logs = list(reversed(tcp_manager.device_logs[device_id]))
                        # Filtre uygula
                        if search_q:
                            all_logs = [m for m in all_logs if (search_q in str(m.get('user_name') or '').lower()) or (search_q in str(m.get('device_name') or '').lower()) or (search_q in str(m.get('card_uuid') or '').lower()) or (search_q in str(m.get('device_id') or '').lower())]
                        if filter_type:
                            all_logs = [m for m in all_logs if str(m.get('type_label') or '').upper() == filter_type or str(m.get('type') or '').upper() == filter_type]
                        if start_date_str or end_date_str:
                            from datetime import datetime as _dt
                            sdate = None; edate = None
                            try:
                                if start_date_str:
                                    sdate = _dt.strptime(start_date_str[:10], '%Y-%m-%d').date()
                            except Exception:
                                sdate = None
                            try:
                                if end_date_str:
                                    edate = _dt.strptime(end_date_str[:10], '%Y-%m-%d').date()
                            except Exception:
                                edate = None
                            def in_range2(ts):
                                try:
                                    d = _dt.strptime(str(ts), '%d-%m-%y %H:%M:%S').date()
                                except Exception:
                                    return True
                                if sdate and d < sdate:
                                    return False
                                if edate and d > edate:
                                    return False
                                return True
                            all_logs = [m for m in all_logs if in_range2(m.get('timestamp'))]
                        # Boş isimleri yerelleştirilmiş 'unknown_user' ile doldur
                        for it in all_logs:
                            if not (it.get('user_name') or '').strip():
                                it['user_name'] = _('unknown_user')
                        total_logs = len(all_logs)
                        start_idx = (page - 1) * per_page
                        end_idx = start_idx + per_page
                        logs = all_logs[start_idx:end_idx]
                        for i, log in enumerate(logs):
                            log['display_index'] = total_logs - start_idx - i
                except Exception:
                    pass
        except Exception as e:
            logger.error(f"device_logs map hata: {e}")
        
        # Toplam sayfa sayısı
        total_pages = (total_logs + per_page - 1) // per_page if total_logs > 0 else 1
        
        return render_template('device_logs.html',
                              device=device,
                              device_id=device_id,
                              logs=logs,
                              total_logs=total_logs,
                              page=page,
                              per_page=per_page,
                              total_pages=total_pages,
                              user=user_info,
                              user_name=user_name,
                              static_bust=int(time.time()),
                              get_page_range=lambda current, total: range(1, min(total, 5)+1),
                              translations=TRANSLATIONS,
                              current_language=session.get('dil', 'tr'))

    # Cihazlar route'u
    @app.route('/devices')
    def devices():
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        logger.info(f"Devices page - User ID: {user_id}, Is Admin: {is_admin}")
        
        # Debug: Kullanıcı bilgilerini kontrol et
        user_info = None
        try:
            # Önce users.db'den dene (profil resmi burada güncel olabilir)
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                urow = uconn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                uconn.close()
                if urow:
                    user_info = urow
            except Exception:
                user_info = None
            # Fallback data.db
            if not user_info:
                conn = get_db_connection()
                user_info = conn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                conn.close()
            if user_info:
                logger.info(f"User found: {user_info['id']} - {user_info['email']} - {user_info['full_name']}")
            else:
                logger.error(f"User with ID {user_id} not found in database!")
        except Exception as e:
            logger.error(f"User lookup error: {e}")
        
        logger.info("Starting to get devices...")
        cihazlar = []
        
        if is_admin:
            logger.info("Getting admin devices...")
            conn = get_db_connection()
            data_devices = conn.execute('SELECT * FROM cihazlar').fetchall()
            conn.close()
            for device in data_devices:
                cihazlar.append({'id': device['id'], 'ad': device['ad'], 'seri_no': device['seri_no'] if device['seri_no'] else None, 'tcp_tipi': device['tcp_tipi'], 'ip_adresi': device['ip_adresi'], 'source': 'data_db'})
        else:
            # Non-admin: yalnızca kendi cihazları
            logger.info("Getting user devices (non-admin) from main database...")
            conn = get_db_connection()
            user_devices = conn.execute('SELECT * FROM cihazlar WHERE user_id = ?', (user_id,)).fetchall()
            conn.close()
            for device in user_devices:
                cihazlar.append({'id': device['id'], 'ad': device['ad'], 'seri_no': device['seri_no'] if device['seri_no'] else device['id'], 'tcp_tipi': device['tcp_tipi'], 'ip_adresi': device['ip_adresi'], 'source': 'data_db'})
        
        logger.info(f"Final cihazlar list: {[c['id'] for c in cihazlar]}")
        
                # En son getdeviceinfo cache'ini al (in-memory). getdeviceinfo gönderimi yapma; sadece cache kullan.
        try:
            cached_map = tcp_manager.get_all_device_info_from_cache() or {}
        except Exception:
            cached_map = {}

        # Cihazların bağlantı durumunu kontrol et ve firmware bilgisini ekle
        for cihaz in cihazlar:
            device_id = str(cihaz['id'])
            logger.info(f"Processing final device: {device_id} - {cihaz.get('ad', 'No name')}")
            cihaz['is_connected'] = False
            try:
                # Sıkı kontrol: aktif bağlantı veya yeni yanıt
                is_conn = tcp_manager.is_device_connected(device_id)
                has_ip = device_id in tcp_manager.connected_device_ips
                cihaz['is_connected'] = bool(is_conn or has_ip)
            except Exception:
                cihaz['is_connected'] = False
            
            # IP adresini al - önce connected_device_ips'den, sonra deviceinfo'dan
            if device_id in tcp_manager.connected_device_ips:
                cihaz['ip_adresi'] = tcp_manager.connected_device_ips[device_id]
            elif not cihaz.get('ip_adresi') or cihaz.get('ip_adresi') == '':
                # deviceinfo tablosundan IP adresini al
                try:
                    conn = get_db_connection()
                    device_info = conn.execute('''
                        SELECT ip_address FROM deviceinfo 
                        WHERE device_id = ?
                    ''', (device_id,)).fetchone()
                    conn.close()
                    
                    if device_info and device_info['ip_address']:
                        cihaz['ip_adresi'] = device_info['ip_address']
                        logger.info(f"IP adresi deviceinfo'dan alındı: {device_id} -> {device_info['ip_address']}")
                except Exception as e:
                    logger.error(f"IP adresi alma hatası: {e}")
            
            # Seri no bilgisini düzelt
            if not cihaz.get('seri_no') and cihaz.get('id'):
                cihaz['seri_no'] = cihaz['id']
            
            # Firmware bilgisini deviceinfo tablosundan al
            try:
                conn = get_db_connection()
                device_info = conn.execute('''
                    SELECT firmware_version, ip_address FROM deviceinfo 
                    WHERE device_id = ?
                ''', (device_id,)).fetchone()
                conn.close()
                
                if device_info:
                    if device_info['firmware_version']:
                        cihaz['firmware_version'] = device_info['firmware_version']
                    else:
                        cihaz['firmware_version'] = 'N/A'
                    
                    # IP adresi yoksa deviceinfo'dan al
                    if not cihaz.get('ip_adresi') and device_info['ip_address']:
                        cihaz['ip_adresi'] = device_info['ip_address']
                else:
                    cihaz['firmware_version'] = 'N/A'
            except Exception as e:
                logger.error(f"Firmware bilgisi alma hatası: {e}")
                cihaz['firmware_version'] = 'N/A'
            
            # Cache'den varsa firmware versiyonunu tercih et
            try:
                cached = cached_map.get(device_id) or {}
                cached_data = cached.get('data') or {}
                ver = cached_data.get('version')
                if ver:
                    cihaz['firmware_version'] = ver
                # IP yoksa cache'den wifidhcp/ethstatic ile tahmin et
                if not cihaz.get('ip_adresi'):
                    wifidhcp = cached_data.get('wifidhcp') or []
                    ethstatic = cached_data.get('ethstatic') or []
                    ip_guess = (wifidhcp[0] if isinstance(wifidhcp, list) and len(wifidhcp) > 0 else None) or (ethstatic[0] if isinstance(ethstatic, list) and len(ethstatic) > 0 else None)
                    if ip_guess:
                        cihaz['ip_adresi'] = ip_guess
            except Exception:
                pass
        
        return render_template('devices.html', 
                             cihazlar=cihazlar,
                             user_name=session.get('user_name'),
                             user={'profile_image': (user_info['profile_image'] if user_info else None), 'company': (user_info['company'] if user_info else None)},
                             is_admin=is_admin,
                             connected_device_ips=tcp_manager.connected_device_ips,
                             device_info_cache=cached_map,
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'),
                             static_bust=int(time.time()))

    # Cihaz ayarları route'u
    @app.route('/device/settings/<device_id>')
    def device_settings(device_id):
        if 'user_id' not in session:
            return redirect(url_for('login'))
        
        user_id = session['user_id']
        
        # Cihaz bilgilerini al - önce ana cihazlar tablosundan
        device_info = None
        try:
            conn = get_db_connection()
            device_info = conn.execute('''
                SELECT * FROM cihazlar WHERE id = ?
            ''', (device_id,)).fetchone()
            conn.close()
        except Exception as e:
            logger.error(f"Ana cihaz tablosu sorgu hatası: {e}")
        
        # Ana tabloda bulunamadıysa user_devices tablosundan ara
        if not device_info:
            try:
                user_devices = user_manager.get_all_user_devices()
                for ud in user_devices:
                    if str(ud['serial_number']) == str(device_id):
                        device_info = {
                            'id': ud['serial_number'],
                            'ad': ud['device_name'],
                            'tcp_tipi': ud['device_type'],
                            'ip_adresi': ud['ip_address'],
                            'seri_no': ud['serial_number']
                        }
                        break
            except Exception as e:
                logger.error(f"User devices sorgu hatası: {e}")
        
        if not device_info:
            logger.warning(f"Cihaz bulunamadı: {device_id}")
            return redirect(url_for('devices'))
        
        # IP adresini al
        ip_address = 'N/A'
        if str(device_id) in tcp_manager.connected_device_ips:
            ip_address = tcp_manager.connected_device_ips[str(device_id)]
        else:
            try:
                conn = get_db_connection()
                device_info_db = conn.execute('''
                    SELECT ip_address FROM deviceinfo WHERE device_id = ?
                ''', (str(device_id),)).fetchone()
                conn.close()
                
                if device_info_db and device_info_db['ip_address']:
                    ip_address = device_info_db['ip_address']
            except Exception as e:
                logger.error(f"IP adresi alma hatası: {e}")
        
        try:
            device_data = {
                'id': str(device_info['id']),
                'device_id': device_info['id'],
                'device_name': device_info['ad'],
                'ip_address': ip_address
            }
            
            return render_template('device_settings.html', device_info=device_data, translations=TRANSLATIONS, current_language=session.get('dil','tr'))
            
        except Exception as e:
            logger.error(f"Device settings hatası: {e}")
            flash('Cihaz bilgileri alınırken hata oluştu!', 'error')
            return redirect(url_for('devices'))

    # Cihaz komut API route'u
    @app.route('/api/device_command/<device_id>', methods=['POST'])
    def device_command(device_id):
        if 'user_id' not in session:
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        
        try:
            data = request.get_json()
            command = data.get('command')
            command_data = data.get('data', {})
            
            if not command:
                return jsonify({'success': False, 'message': 'Komut belirtilmedi'}), 400
            
            # Cihazın kullanıcıya ait olduğunu kontrol et
            user_id = session['user_id']
            conn = get_db_connection()
            device = conn.execute('''
                SELECT * FROM cihazlar WHERE id = ?
            ''', (device_id,)).fetchone()
            conn.close()
            
            if not device:
                current_language = get_locale()
                translations = TRANSLATIONS.get(current_language, TRANSLATIONS['tr'])
                return jsonify({'success': False, 'message': translations['device_not_found']}), 404
            
            # Cihazın bağlı olup olmadığını kontrol et
            if not tcp_manager.is_device_connected(str(device_id)):
                current_language = get_locale()
                translations = TRANSLATIONS.get(current_language, TRANSLATIONS['tr'])
                return jsonify({'success': False, 'message': translations['device_not_connected']}), 400
            
            # Komutu cihaza gönder
            ip_address = tcp_manager.connected_device_ips.get(str(device_id))
            if not ip_address:
                current_language = get_locale()
                translations = TRANSLATIONS.get(current_language, TRANSLATIONS['tr'])
                return jsonify({'success': False, 'message': translations['device_ip_not_found']}), 400
            
            # JSON komutunu oluştur
            if command == 'setwifissid' and 'setwifipassword' in command_data:
                # WiFi SSID ve password aynı anda gönderiliyor - doğrudan command_data'yı kullan
                json_command = command_data
            elif command == 'setwifissid':
                # Sadece SSID gönderiliyor
                json_command = {command: command_data.get('setwifissid', '')}
            else:
                json_command = {command: command_data}
            command_string = json.dumps(json_command)
            
            # TCP üzerinden komutu gönder
            success = tcp_manager.send_command_to_device(str(device_id), command_string)
            
            if success:
                # Cihazdan yanıt bekle
                response = tcp_manager.get_command_response(str(device_id), timeout=15)
                
                if response:
                    if response['status'] == 'success':
                        # Başarılı komut sonrası arka planda getdeviceinfo gönder
                        threading.Thread(target=lambda: tcp_manager.send_command_to_device(str(device_id), "getdeviceinfo"), daemon=True).start()
                        return jsonify({'success': True, 'message': response['message']})
                    else:
                        return jsonify({'success': False, 'message': response['message']}), 400
                else:
                    # Yanıt gelmese de arka planda getdeviceinfo gönder
                    threading.Thread(target=lambda: tcp_manager.send_command_to_device(str(device_id), "getdeviceinfo"), daemon=True).start()
                    return jsonify({'success': True, 'message': f'{command} komutu gönderildi (yanıt bekleniyor)'})
            else:
                return jsonify({'success': False, 'message': 'Komut gönderilemedi'}), 500
            
        except Exception as e:
            logger.error(f"Device command hatası: {e}")
            return jsonify({'success': False, 'message': f'Hata: {str(e)}'}), 500

    # Cihaz bilgi API route'u
    @app.route('/api/deviceinfo/<device_id>')
    def get_device_info_details(device_id):
        """Cihazdan gelen tüm verileri getir"""
        if not session.get('logged_in'):
            return jsonify({'error': 'Unauthorized'}), 401
        
        user_id = session.get('user_id')
        is_admin = session.get('is_admin', False)
        
        try:
            conn = get_db_connection()
            
            # Önce deviceinfo tablosunda ara
            device_info = conn.execute('''
                SELECT * FROM deviceinfo 
                WHERE device_id = ? 
            ''', (device_id,)).fetchone()
            
            # Eğer deviceinfo'da yoksa, farklı yollarla eşleştir
            if not device_info:
                mapped = False
                # 1) cihazlar tablosunda id veya seri_no ile eşleştir
                try:
                    drow = conn.execute('SELECT id, ad, seri_no, ip_adresi FROM cihazlar WHERE seri_no = ? LIMIT 1', (device_id,)).fetchone()
                    if not drow and str(device_id).isdigit():
                        drow = conn.execute('SELECT id, ad, seri_no, ip_adresi FROM cihazlar WHERE id = ? LIMIT 1', (device_id,)).fetchone()
                    if drow:
                        device_info = {
                            'id': drow['id'],
                            'device_id': drow['seri_no'] if drow['seri_no'] else str(drow['id']),
                            'ip_address': drow['ip_adresi'],
                            'firmware_version': 'N/A',
                            'status': 'unknown',
                            'last_update': None,
                            'device_data': '{}'
                        }
                        mapped = True
                except Exception:
                    mapped = False
                # 2) Kullanıcı cihazları (admin: tümü; değilse: kendi cihazları)
                if not mapped:
                    try:
                        from user_management import UserManager
                        user_manager = UserManager()
                        user_devices = user_manager.get_all_user_devices() if is_admin else user_manager.get_user_devices(user_id)
                        user_device = None
                        for dev in user_devices:
                            if str(dev.get('serial_number')) == str(device_id):
                                user_device = dev
                                break
                        if user_device:
                            device_info = {
                                'id': user_device.get('id') or user_device.get('serial_number'),
                                'device_id': user_device.get('serial_number'),
                                'ip_address': user_device.get('ip_address'),
                                'firmware_version': 'N/A',
                                'status': 'unknown',
                                'last_update': user_device.get('created_at'),
                                'device_data': '{}'
                            }
                            mapped = True
                    except Exception:
                        mapped = False
                if not mapped:
                    return jsonify({'success': False, 'message': 'Device not found'}), 404
            
            # Cihaz getdeviceinfo verisini cache'den al
            cached = tcp_manager.get_device_info_from_cache(device_id)
            device_data = cached['data'] if cached and 'data' in cached else {}
            
            # Cihazın IP adresini al
            device_ip = None
            if device_info['ip_address']:
                device_ip = device_info['ip_address']
            elif device_id in tcp_manager.connected_device_ips:
                device_ip = tcp_manager.connected_device_ips[device_id]
            
            # JSON log toplama kaldırıldı; device_data sadece cache'den geliyor
            
            conn.close()
            
            # Cihaz bağlantı durumunu kontrol et
            is_connected = tcp_manager.is_device_connected(device_id)
            
            # Detaylı bilgileri hazırla
            detailed_info = {
                'device_info': {
                    'id': device_info['id'],
                    'device_id': device_info['device_id'],
                    'ip_address': device_info['ip_address'] if device_info['ip_address'] else None,
                    'firmware_version': device_info['firmware_version'] if device_info['firmware_version'] else 'N/A',
                    'status': device_info['status'] if device_info['status'] else 'unknown',
                    'last_update': device_info['last_update'] if device_info['last_update'] else None,
                    'is_connected': is_connected
                },
                'device_data': device_data,
                # 'json_logs' kaldırıldı,
                'connection_info': {
                    'in_client_connections': device_id in tcp_manager.client_connections,
                    'in_response_status': device_id in tcp_manager.device_response_status,
                    'last_response': None
                }
            }
            
            # Son yanıt zamanını al
            if device_id in tcp_manager.device_response_status:
                last_response = tcp_manager.device_response_status[device_id].get('last_response')
                if last_response:
                    detailed_info['connection_info']['last_response'] = datetime.fromtimestamp(last_response).strftime('%Y-%m-%d %H:%M:%S')
            
            return jsonify({
                'success': True,
                'info': detailed_info
            })
            
        except Exception as e:
            logger.error(f"Device info details hatası: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500
    


            
    @app.route('/api/delete-all-logs', methods=['POST'])
    def api_delete_all_logs():
        try:
            data = request.get_json(silent=True) or {}
            source = str(data.get('source', 'central')).strip().lower()
            if source == 'device':
                device_id = str(data.get('device_id', '')).strip()
                # Belirli cihaz varsa onu temizle, yoksa tüm cihaz bellek loglarını temizle
                if device_id and hasattr(tcp_manager, 'device_logs') and device_id in tcp_manager.device_logs:
                    tcp_manager.device_logs[device_id] = []
                elif hasattr(tcp_manager, 'device_logs'):
                    tcp_manager.device_logs.clear()
                return jsonify({'success': True})
            # Varsayılan: merkezi access_logs'u temizle
            conn = get_db_connection()
            # Mobil not eşlemelerini de temizle
            try:
                conn.execute('CREATE TABLE IF NOT EXISTS mobile_access_notes (access_log_id INTEGER PRIMARY KEY, note TEXT)')
            except Exception:
                pass
            conn.execute('DELETE FROM access_logs')
            conn.execute('DELETE FROM mobile_access_notes')
            conn.commit()
            conn.close()
            return jsonify({'success': True})
        except Exception as e:
            logger.error(f"/api/delete-all-logs hata: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/delete-log', methods=['POST'])
    def api_delete_log():
        try:
            data = request.get_json(silent=True) or {}
            log_id = data.get('log_id')
            source = str(data.get('source', 'central')).strip().lower()
            if source == 'device':
                device_id = str(data.get('device_id', '')).strip()
                if not device_id:
                    return jsonify({'success': False, 'message': 'device_id gerekli'}), 400
                if not hasattr(tcp_manager, 'device_logs') or device_id not in tcp_manager.device_logs:
                    return jsonify({'success': False, 'message': 'Cihaz logları bulunamadı'}), 404
                # display_index mantığı: display_index = total - index_in_full
                try:
                    total = len(tcp_manager.device_logs[device_id])
                    index_in_full = total - int(log_id)
                    if 0 <= index_in_full < total:
                        del tcp_manager.device_logs[device_id][index_in_full]
                        return jsonify({'success': True})
                    return jsonify({'success': False, 'message': 'Geçersiz log id'}), 400
                except Exception as e:
                    return jsonify({'success': False, 'message': f'Hata: {e}'}), 400
            # Varsayılan: merkezi access_logs kaydını sil
            if log_id is None:
                return jsonify({'success': False, 'message': 'log_id gerekli'}), 400
            conn = get_db_connection()
            conn.execute('DELETE FROM access_logs WHERE id = ?', (log_id,))
            try:
                conn.execute('DELETE FROM mobile_access_notes WHERE access_log_id = ?', (log_id,))
            except Exception:
                pass
            conn.commit()
            conn.close()
            return jsonify({'success': True})
        except Exception as e:
            logger.error(f"/api/delete-log hata: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/profile', methods=['GET', 'POST'])
    def profile():
        """Profil sayfası"""
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        
        if request.method == 'POST':
            # Profil güncelleme
            full_name = request.form.get('full_name', '').strip()
            email = request.form.get('email', '').strip()
            company = request.form.get('company', '').strip()
            phone = request.form.get('phone', '').strip()
            timezone = request.form.get('timezone', 'Europe/Istanbul')
            language = request.form.get('language', 'tr')
            
            # Profil resmi güncelleme
            profile_image = None
            if 'profile_image' in request.files:
                file = request.files['profile_image']
                if file and file.filename:
                    import os
                    import uuid
                    from werkzeug.utils import secure_filename
                    
                    # Dosya uzantısını kontrol et
                    allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
                    if '.' in file.filename and file.filename.rsplit('.', 1)[1].lower() in allowed_extensions:
                        # Benzersiz dosya adı oluştur
                        filename = secure_filename(file.filename)
                        file_extension = filename.rsplit('.', 1)[1].lower()
                        unique_filename = f"{user_id}_{uuid.uuid4().hex}.{file_extension}"
                        
                        # Uploads klasörünü oluştur
                        upload_folder = 'static/uploads'
                        if not os.path.exists(upload_folder):
                            os.makedirs(upload_folder)
                        
                        # Dosyayı kaydet
                        file_path = os.path.join(upload_folder, unique_filename)
                        file.save(file_path)
                        profile_image = unique_filename
                        
                        # Profil resmi güncellendi, flash mesajı vermeye gerek yok
            
            # Şifre değiştirme
            current_password = request.form.get('current_password', '')
            new_password = request.form.get('new_password', '')
            confirm_password = request.form.get('confirm_password', '')
            
            try:
                conn = sqlite3.connect('users.db')
                cursor = conn.cursor()
                
                # Şifre değişikliği kontrolü
                if current_password and new_password:
                    # Mevcut şifreyi kontrol et
                    cursor.execute('SELECT password_hash FROM users WHERE id = ?', (user_id,))
                    current_hash = cursor.fetchone()[0]
                    
                    if user_manager.hash_password(current_password) != current_hash:
                        flash(_('current_password_incorrect'), 'error')
                        conn.close()
                        return redirect(url_for('profile'))
                    
                    if new_password != confirm_password:
                        flash(_('passwords_dont_match'), 'error')
                        conn.close()
                        return redirect(url_for('profile'))
                    
                    if len(new_password) < 6:
                        flash(_('password_too_short'), 'error')
                        conn.close()
                        return redirect(url_for('profile'))
                    
                    # Şifreyi güncelle
                    new_hash = user_manager.hash_password(new_password)
                    update_fields = [full_name, email, company, phone, timezone, new_hash]
                    update_sql = '''
                        UPDATE users 
                        SET full_name = ?, email = ?, company = ?, phone = ?, timezone = ?, password_hash = ?
                    '''
                    
                    if profile_image:
                        update_sql += ', profile_image = ?'
                        update_fields.append(profile_image)
                    
                    update_sql += ' WHERE id = ?'
                    update_fields.append(user_id)
                    
                    cursor.execute(update_sql, update_fields)
                    flash(_('profile_password_updated'), 'success')
                else:
                    # Sadece profil bilgilerini güncelle
                    update_fields = [full_name, email, company, phone, timezone]
                    update_sql = '''
                        UPDATE users 
                        SET full_name = ?, email = ?, company = ?, phone = ?, timezone = ?
                    '''
                    
                    if profile_image:
                        update_sql += ', profile_image = ?'
                        update_fields.append(profile_image)
                    
                    update_sql += ' WHERE id = ?'
                    update_fields.append(user_id)
                    
                    cursor.execute(update_sql, update_fields)
                    flash(_('profile_updated'), 'success')
                
                conn.commit()
                conn.close()
                
                # data.db ile profil bilgisini senkronize et (özellikle profile_image ve company)
                try:
                    dconn = sqlite3.connect('data.db')
                    dcur = dconn.cursor()
                    if profile_image:
                        dcur.execute('UPDATE users SET full_name = COALESCE(?, full_name), email = COALESCE(?, email), company = COALESCE(?, company), phone = COALESCE(?, phone), profile_image = ? WHERE id = ?', (full_name or None, email or None, company or None, phone or None, profile_image, user_id))
                    else:
                        dcur.execute('UPDATE users SET full_name = COALESCE(?, full_name), email = COALESCE(?, email), company = COALESCE(?, company), phone = COALESCE(?, phone) WHERE id = ?', (full_name or None, email or None, company or None, phone or None, user_id))
                    dconn.commit(); dconn.close()
                except Exception as _se:
                    logger.warning(f"profile sync to data.db warn: {_se}")
                
                # Oturum bilgilerini güncelle
                session['user_name'] = full_name or session.get('user_name')
                session['user_email'] = email or session.get('user_email')
                session['dil'] = language or session.get('dil', 'tr')
                return redirect(url_for('profile'))
            except Exception as e:
                flash(f'Profil güncellenemedi: {str(e)}', 'error')
                return redirect(url_for('profile'))
        
        # GET: mevcut kullanıcı bilgilerini getir
        conn = sqlite3.connect('users.db')
        conn.row_factory = sqlite3.Row
        user = conn.execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()
        conn.close()
        
        return render_template('profile.html', user=user, 
                             translations=TRANSLATIONS,
                             current_language=session.get('dil', 'tr'))
    
    # Kullanıcı ekleme route'u
    @app.route('/remove-profile-image', methods=['POST'])
    def remove_profile_image():
        if 'user_id' not in session:
            return redirect(url_for('login'))
        
        user_id = session['user_id']
        
        try:
            conn = sqlite3.connect('data.db')
            cursor = conn.cursor()
            
            # Mevcut profil resmini al
            cursor.execute('SELECT profile_image FROM users WHERE id = ?', (user_id,))
            result = cursor.fetchone()
            
            if result and result[0]:
                # Dosyayı sil
                profile_image_path = os.path.join('static/uploads', result[0])
                if os.path.exists(profile_image_path):
                    os.remove(profile_image_path)
                
                # Veritabanından kaldır
                cursor.execute('UPDATE users SET profile_image = NULL WHERE id = ?', (user_id,))
                conn.commit()
                
                flash('Profil resmi başarıyla kaldırıldı', 'success')
            else:
                flash('Profil resmi bulunamadı', 'error')
            
            conn.close()
            
        except Exception as e:
            flash(f'Profil resmi kaldırılırken hata: {str(e)}', 'error')
        
        return redirect(url_for('profile'))

    @app.route('/delete-account', methods=['POST'])
    def delete_account():
        """Kullanıcı hesabını ve tüm ilişkili verileri her iki veritabanından siler"""
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        user_id = session.get('user_id')
        user_email = session.get('user_email')
        
        try:
            # 1) data.db tarafı: kullanıcı cihazları, access_logs, device_cards ve user kaydı
            dconn = get_db_connection(); dconn.row_factory = sqlite3.Row
            dcur = dconn.cursor()
            # Cihaz ID havuzu (string set)
            device_id_set = set()
            # Kullanıcının data.db'deki id'sini email ile de bul (id uyuşmazlıklarına karşı)
            data_user_id = None
            try:
                du = dcur.execute('SELECT id FROM users WHERE lower(email)=?', ((user_email or '').strip().lower(),)).fetchone()
                if du:
                    data_user_id = int(du['id'])
            except Exception:
                pass
            # Kullanıcının sahip olduğu cihazlar (hem session id hem data_user_id)
            id_candidates = [user_id]
            if data_user_id and int(data_user_id) != int(user_id):
                id_candidates.append(data_user_id)
            unique_ids = list(dict.fromkeys([int(x) for x in id_candidates if x is not None]))
            for uid in unique_ids:
                try:
                    rows = dcur.execute('SELECT id FROM cihazlar WHERE user_id = ?', (uid,)).fetchall()
                    for r in rows:
                        device_id_set.add(str(r['id']))
                except Exception:
                    pass
            # users.db üzerinden bu kullanıcıya atanmış cihazlar
            try:
                uconn_assign = sqlite3.connect('users.db'); uconn_assign.row_factory = sqlite3.Row
                ucur_assign = uconn_assign.cursor()
                # users.db'deki id'yi email ile bulalım
                urow_id = ucur_assign.execute('SELECT id FROM users WHERE lower(email)=?', ((user_email or '').strip().lower(),)).fetchone()
                if urow_id:
                    uid_usersdb = int(urow_id['id'])
                    ass_rows = ucur_assign.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (uid_usersdb,)).fetchall()
                    for ar in ass_rows:
                        did = str(ar['device_id'])
                        if did:
                            device_id_set.add(did)
                uconn_assign.close()
            except Exception:
                pass
            device_ids = list(device_id_set)
            if device_ids:
                marks = ','.join(['?'] * len(device_ids))
                dcur.execute(f'DELETE FROM access_logs WHERE device_id IN ({marks})', device_ids)
                try:
                    dcur.execute(f'DELETE FROM device_cards WHERE device_id IN ({marks})', device_ids)
                except Exception:
                    pass
                dcur.execute(f'DELETE FROM cihazlar WHERE id IN ({marks})', device_ids)
                try:
                    dcur.execute(f'DELETE FROM deviceinfo WHERE device_id IN ({marks})', device_ids)
                except Exception:
                    pass
            # Bu kullanıcının cihazlarına atanmış (ana hesabın oluşturduğu) alt kullanıcıları sil
            try:
                if device_ids:
                    uconn_ch = sqlite3.connect('users.db'); uconn_ch.row_factory = sqlite3.Row
                    placeholders = ','.join(['?'] * len(device_ids))
                    child_rows = uconn_ch.execute(f'''SELECT DISTINCT uda.user_id FROM user_device_access uda
                                                      WHERE uda.device_id IN ({placeholders})''', device_ids).fetchall()
                    child_ids = [int(r['user_id']) for r in child_rows if int(r['user_id']) != int(user_id)]
                    if child_ids:
                        cmarks = ','.join(['?'] * len(child_ids))
                        # Tüm alt kullanıcıları hedefle (admin dahil)
                        target_ids = [r['id'] for r in uconn_ch.execute(f'''SELECT id FROM users WHERE id IN ({cmarks})''', child_ids).fetchall()]
                        if target_ids:
                            namarks = ','.join(['?'] * len(target_ids))
                            # users.db tarafı temizliği
                            for table in ['user_access_logs','user_devices','user_device_access']:
                                try:
                                    uconn_ch.execute(f"DELETE FROM {table} WHERE user_id IN ({namarks})", target_ids)
                                except Exception:
                                    pass
                            # Avatar dosyaları
                            try:
                                for row in uconn_ch.execute(f"SELECT profile_image FROM users WHERE id IN ({namarks})", target_ids).fetchall():
                                    pi = row['profile_image']
                                    if pi and not str(pi).startswith('@'):
                                        import os
                                        pth = os.path.join('static','uploads',pi)
                                        if os.path.exists(pth):
                                            os.remove(pth)
                            except Exception:
                                pass
                            uconn_ch.execute(f"DELETE FROM users WHERE id IN ({namarks})", target_ids)
                            uconn_ch.commit()
                            # data.db'den de bu kullanıcıları sil
                            try:
                                dconn2 = get_db_connection(); dconn2.row_factory = sqlite3.Row
                                dmarks = ','.join(['?'] * len(target_ids))
                                dconn2.execute(f"DELETE FROM users WHERE id IN ({dmarks})", target_ids)
                                dconn2.commit(); dconn2.close()
                            except Exception:
                                pass
                    uconn_ch.close()
            except Exception as cherr:
                logger.error(f"Alt kullanıcı silme hatası: {cherr}")
            # Profil resmi dosyası (statik değilse) ve kullanıcı kaydı
            try:
                urow = dcur.execute('SELECT profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                if urow and urow['profile_image'] and not str(urow['profile_image']).startswith('@'):
                    import os
                    p = os.path.join('static', 'uploads', urow['profile_image'])
                    if os.path.exists(p):
                        os.remove(p)
            except Exception:
                pass
            dcur.execute('DELETE FROM users WHERE id = ?', (user_id,))
            dconn.commit(); dconn.close()
            
            # 2) users.db tarafı: user_devices, user_access_logs, user_device_access, users
            uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
            ucur = uconn.cursor()
            # Agresif: Bu kullanıcı tarafından oluşturulan tüm kullanıcılar (created_by=user_id)
            try:
                created_rows = ucur.execute('SELECT id, profile_image FROM users WHERE created_by = ?', (user_id,)).fetchall()
                created_ids = [int(r['id']) for r in created_rows]
                if created_ids:
                    marks = ','.join(['?'] * len(created_ids))
                    # İlişkili kayıtları sil
                    for table in ['user_access_logs', 'user_devices', 'user_device_access']:
                        try:
                            ucur.execute(f'DELETE FROM {table} WHERE user_id IN ({marks})', created_ids)
                        except Exception:
                            pass
                    # Profil resimleri
                    try:
                        import os
                        for r in created_rows:
                            pi = r['profile_image']
                            if pi and not str(pi).startswith('@'):
                                pth = os.path.join('static','uploads',pi)
                                if os.path.exists(pth):
                                    os.remove(pth)
                    except Exception:
                        pass
                    # users.db kullanıcı sil
                    ucur.execute(f'DELETE FROM users WHERE id IN ({marks})', created_ids)
                    # data.db tarafında da aynı id'leri sil
                    try:
                        dconn3 = get_db_connection(); dconn3.row_factory = sqlite3.Row
                        dmarks = ','.join(['?'] * len(created_ids))
                        dconn3.execute(f'DELETE FROM users WHERE id IN ({dmarks})', created_ids)
                        dconn3.commit(); dconn3.close()
                    except Exception:
                        pass
            except Exception:
                pass
            for table in ['user_access_logs', 'user_devices', 'user_device_access']:
                try:
                    ucur.execute(f'DELETE FROM {table} WHERE user_id = ?', (user_id,))
                except Exception:
                    pass
            try:
                urow2 = ucur.execute('SELECT profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                if urow2 and urow2['profile_image'] and not str(urow2['profile_image']).startswith('@'):
                    import os
                    p2 = os.path.join('static', 'uploads', urow2['profile_image'])
                    if os.path.exists(p2):
                        os.remove(p2)
            except Exception:
                pass
            ucur.execute('DELETE FROM users WHERE id = ?', (user_id,))
            uconn.commit(); uconn.close()
            
            # Oturum temizle
            session.clear()
            flash('Hesabınız ve tüm verileriniz başarıyla silindi', 'success')
            return redirect(url_for('login'))
        except Exception as e:
            logger.error(f"delete_account hata: {e}")
            flash(f'Hesap silme hatası: {str(e)}', 'error')
            return redirect(url_for('profile'))

    # Kullanıcı silme route'u (Basit database silme)
    @app.route('/delete-user/<int:user_id>', methods=['POST'])
    def delete_user(user_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        
        try:
            # Sadece database'den sil - cihaz işlemi yok
            import sqlite3
            users_conn = sqlite3.connect('users.db')
            users_conn.row_factory = sqlite3.Row
            
            # Silinecek kullanıcıyı kontrol et
            target_user = users_conn.execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()
            if not target_user:
                users_conn.close()
                return jsonify({'success': False, 'message': 'Kullanıcı bulunamadı'}), 404
            
            # MASTERCARD kullanıcılarının otomatik yeniden oluşmasını engellemek için blocklist'e ekle
            try:
                if (target_user['full_name'] or '').upper() == 'MASTERCARD':
                    dconn = get_db_connection(); dconn.row_factory = sqlite3.Row
                    dconn.execute('CREATE TABLE IF NOT EXISTS device_cards (device_id TEXT, card_uuid TEXT, user_name TEXT, authority_zone TEXT, terminal_mode_access INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)')
                    # Bu kullanıcının card_uuid'sini al
                    cu = target_user['card_uuid'] if 'card_uuid' in target_user.keys() else None
                    # Kullanıcı atanmış cihazlarını bul
                    dev_rows = users_conn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
                    dconn.close()
            except Exception as blerr:
                logger.error(f"Blocklist ekleme hatası: {blerr}")

            # Kullanıcının NFC kartını atanmış cihazlardan sil (deletecards)
            try:
                card_uuid_val = None
                try:
                    card_uuid_val = target_user['card_uuid'] if 'card_uuid' in target_user.keys() else None
                except Exception:
                    card_uuid_val = None
                if card_uuid_val:
                    # Atanmış cihazları al
                    assigned_rows = users_conn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
                    assigned_devices = [str(r['device_id']) for r in assigned_rows] if assigned_rows else []
                    def to_hex(u):
                        s = str(u or '').strip()
                        if not s:
                            return None
                        if s.lower().startswith('0x'):
                            return s
                        try:
                            n = int(s, 10)
                            return '0x' + format(n, 'x')
                        except Exception:
                            return None
                    hex_uuid = to_hex(card_uuid_val)
                    if hex_uuid and assigned_devices:
                        for dev_id in assigned_devices:
                            try:
                                if tcp_manager.is_device_connected(str(dev_id)):
                                    payload = json.dumps({'deletecards': [hex_uuid]})
                                    ok = tcp_manager.send_command_to_device(str(dev_id), payload)
                                    if ok:
                                        resp = tcp_manager.get_command_response(str(dev_id), timeout=10)
                                        try:
                                            m = json.dumps(resp).lower() if isinstance(resp, dict) else ''
                                            if 'saveok' in m or 'setok' in m:
                                                tcp_manager.send_command_to_device(str(dev_id), 'updatecards')
                                        except Exception:
                                            pass
                                # DB'den de kaldır (varsa)
                                try:
                                    dconn2 = get_db_connection(); dconn2.row_factory = sqlite3.Row
                                    dconn2.execute('DELETE FROM device_cards WHERE device_id = ? AND card_uuid IN (?,?)', (str(dev_id), hex_uuid, str(int(card_uuid_val)) if str(card_uuid_val).isdigit() else hex_uuid))
                                    dconn2.commit(); dconn2.close()
                                except Exception:
                                    pass
                            except Exception:
                                continue
            except Exception as _del_push_err:
                logger.error(f"NFC deletecards push hata: {_del_push_err}")

            # Profil resmini sil (yerel dosya)
            if target_user['profile_image'] and not str(target_user['profile_image']).startswith('@'):
                import os
                profile_path = os.path.join('static', 'uploads', target_user['profile_image'])
                if os.path.exists(profile_path):
                    os.remove(profile_path)
            
            # Kullanıcıyı sil
            users_conn.execute('DELETE FROM users WHERE id = ?', (user_id,))
            users_conn.commit()
            users_conn.close()
            
            return jsonify({'success': True, 'message': 'Kullanıcı başarıyla silindi'})
            
        except Exception as e:
            logger.error(f"Kullanıcı silme hatası: {e}")
            return jsonify({'success': False, 'message': f'Silme hatası: {str(e)}'}), 500

    # Kullanıcı düzenleme route'u
    @app.route('/edit-user/<int:user_id>', methods=['GET', 'POST'])
    def edit_user(user_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        
        import sqlite3
        users_conn = sqlite3.connect('users.db')
        users_conn.row_factory = sqlite3.Row
        
        if request.method == 'GET':
            # Kullanıcı bilgilerini getir
            user = users_conn.execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()
            # Bu kullanıcının atanmış cihazlarını getir
            try:
                users_conn.execute('''
                    CREATE TABLE IF NOT EXISTS user_device_access (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        user_id INTEGER NOT NULL,
                        device_id TEXT NOT NULL
                    )
                ''')
            except Exception as _:
                pass
            rows = users_conn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
            preselected_devices = [str(r['device_id']) for r in rows]
            users_conn.close()
            
            if not user:
                flash('Kullanıcı bulunamadı', 'error')
                return redirect(url_for('users'))
            
            return render_template('edit_user.html', 
                                 user=user,
                                 preselected_devices_json=json.dumps(preselected_devices),
                                 translations=TRANSLATIONS,
                                 current_language=session.get('dil', 'tr'))
        
        elif request.method == 'POST':
            try:
                # Kullanıcı bilgilerini getir (admin kontrolü için)
                user = users_conn.execute('SELECT * FROM users WHERE id = ?', (user_id,)).fetchone()
                if not user:
                    flash('Kullanıcı bulunamadı', 'error')
                    return redirect(url_for('users'))
                
                # Form verilerini al
                full_name = request.form.get('full_name', '').strip()
                company = request.form.get('company', '').strip()
                phone = request.form.get('phone', '').strip()
                user_type = request.form.get('user_type', 'standard')
                
                # Kullanıcı durumu (aktif/pasif) - admin kullanıcılar için her zaman aktif
                is_active = True  # Varsayılan olarak aktif
                # sqlite3.Row erişimi
                try:
                    is_admin_value = int(user['is_admin']) if 'is_admin' in user.keys() else 0
                except Exception:
                    is_admin_value = 0
                if not is_admin_value:  # Admin olmayan kullanıcılar için form'dan al
                    is_active = (request.form.get('is_active') == 'on')
                else:
                    is_active = True
                logger.info(f"User is_admin: {is_admin_value}, form is_active: {request.form.get('is_active')}, final is_active: {is_active}")
                
                # Erişim türlerini al
                access_types = request.form.getlist('access_types')
                access_type = ','.join(access_types) if access_types else 'mobile'
                
                # UUID'leri al
                card_uuid = request.form.get('card_uuid', '').strip()
                user_uuid = request.form.get('user_uuid', '').strip()
                mobile_password = request.form.get('mobile_password', '').strip()
                
                # Mobil UUID doğrulama: sadece rakam ve en fazla 16 karakter
                if user_uuid:
                    filtered_uuid = ''.join(ch for ch in user_uuid if ch.isdigit())[:16]
                    if filtered_uuid != user_uuid:
                        flash('Mobil UUID yalnızca rakamlardan oluşmalı ve en fazla 16 hane olmalı', 'error')
                        return redirect(url_for('edit_user', user_id=user_id))
                
                # Zaman sınırlarını al
                start_date = request.form.get('start_date', '').strip()
                start_time = request.form.get('start_time', '').strip()
                end_date = request.form.get('end_date', '').strip()
                end_time = request.form.get('end_time', '').strip()
                
                logger.info(f"Date/time values: start_date={start_date}, start_time={start_time}, end_date={end_date}, end_time={end_time}")
                
                                # Tarih ve saati birleştir
                time_limit_start = None
                time_limit_end = None
                
                if start_date and start_time:
                    time_limit_start = f"{start_date}T{start_time}"
                    logger.info(f"Combined start: {time_limit_start}")

                if end_date and end_time:
                    time_limit_end = f"{end_date}T{end_time}"
                    logger.info(f"Combined end: {time_limit_end}")
                
                # Seçili cihazları al
                selected_devices = request.form.getlist('selected_devices')
                logger.info(f"Selected devices from form: {selected_devices}")
                
                # Profil resmi işleme
                current_user = users_conn.execute('SELECT profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                current_profile_image = current_user['profile_image'] if current_user else None
                # Varsayılan olarak mevcut resmi koru
                profile_image_filename = current_profile_image
                
                if 'profile_image' in request.files:
                    file = request.files['profile_image']
                    if file and file.filename:
                        allowed_extensions = {'png', 'jpg', 'jpeg', 'gif'}
                        file_ext = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else ''
                        if file_ext in allowed_extensions:
                            import uuid
                            import os
                            unique_filename = f"{uuid.uuid4().hex}.{file_ext}"
                            upload_folder = os.path.join('static', 'uploads')
                            os.makedirs(upload_folder, exist_ok=True)
                            file_path = os.path.join(upload_folder, unique_filename)
                            file.save(file_path)
                            profile_image_filename = unique_filename
                            
                            # Eski profil resmini sil
                            if current_profile_image:
                                old_profile_path = os.path.join('static', 'uploads', current_profile_image)
                                if os.path.exists(old_profile_path):
                                    os.remove(old_profile_path)
                
                # Kullanıcı bilgilerini güncelle (opsiyonel şifre güncelleme)
                if mobile_password:
                    new_hash = generate_password_hash(mobile_password)
                    users_conn.execute('''
                        UPDATE users 
                        SET full_name = ?, company = ?, phone = ?, user_type = ?, access_type = ?, 
                            card_uuid = ?, user_uuid = ?, time_limit_start = ?, time_limit_end = ?, 
                            profile_image = ?, is_active = ?, password_hash = ?
                        WHERE id = ?
                    ''', (full_name, company, phone, user_type, access_type, 
                          card_uuid, user_uuid, time_limit_start, time_limit_end, 
                          profile_image_filename, is_active, new_hash, user_id))
                else:
                    users_conn.execute('''
                        UPDATE users 
                        SET full_name = ?, company = ?, phone = ?, user_type = ?, access_type = ?, 
                            card_uuid = ?, user_uuid = ?, time_limit_start = ?, time_limit_end = ?, 
                            profile_image = ?, is_active = ?
                        WHERE id = ?
                    ''', (full_name, company, phone, user_type, access_type, 
                          card_uuid, user_uuid, time_limit_start, time_limit_end, 
                          profile_image_filename, is_active, user_id))
                
                # Cihaz atamalarını güncelle
                try:
                    users_conn.execute('''
                        CREATE TABLE IF NOT EXISTS user_device_access (
                            id INTEGER PRIMARY KEY AUTOINCREMENT,
                            user_id INTEGER NOT NULL,
                            device_id TEXT NOT NULL
                        )
                    ''')
                    users_conn.execute('''
                        CREATE UNIQUE INDEX IF NOT EXISTS idx_user_device_unique
                        ON user_device_access(user_id, device_id)
                    ''')
                    # Eski atamaları sil
                    users_conn.execute('DELETE FROM user_device_access WHERE user_id = ?', (user_id,))
                    # Yeni atamaları ekle
                    for dev_id in selected_devices:
                        if dev_id:
                            users_conn.execute('INSERT OR IGNORE INTO user_device_access (user_id, device_id) VALUES (?, ?)', (user_id, str(dev_id)))
                except Exception as map_err:
                    logger.error(f"Device assignment error (edit_user): {map_err}")
                
                users_conn.commit()
                
                # Kullanıcının NFC kartı varsa ve cihazlar atanmışsa addcards ile cihazlara gönder
                try:
                    if card_uuid and selected_devices:
                        def to_hex(u):
                            s = str(u or '').strip()
                            if not s:
                                return None
                            if s.lower().startswith('0x'):
                                return s
                            try:
                                n = int(s, 10)
                                return '0x' + format(n, 'x')
                            except Exception:
                                return None
                        hex_uuid = to_hex(card_uuid)
                        if hex_uuid:
                            for dev_id in selected_devices:
                                try:
                                    if tcp_manager.is_device_connected(str(dev_id)):
                                        payload = json.dumps({'addcards': [hex_uuid]})
                                        ok = tcp_manager.send_command_to_device(str(dev_id), payload)
                                        if ok:
                                            tcp_manager.get_command_response(str(dev_id), timeout=5)
                                except Exception:
                                    continue
                except Exception as _push_err:
                    logger.error(f"Edit user NFC push hata: {_push_err}")

                users_conn.close()
                
                flash('Kullanıcı başarıyla güncellendi', 'success')
                return redirect(url_for('users'))
                
            except Exception as e:
                logger.error(f"Edit user error: {e}")
                import traceback
                logger.error(f"Edit user traceback: {traceback.format_exc()}")
                flash(f"Güncelleme hatası: {str(e)}", 'error')
                return redirect(url_for('users'))

    # Kart listesi alma (placeholder)
    @app.route('/api/get_cards', methods=['POST'])
    def api_get_cards():
        try:
            if not session.get('logged_in'):
                return jsonify({'status': 'error', 'message': 'Unauthorized'}), 401
            # Şimdilik kart kaynağı olmadığı için boş liste dönüyoruz
            return jsonify({'status': 'success', 'cards': []})
        except Exception as e:
            logger.error(f"/api/get_cards error: {e}")
            return jsonify({'status': 'error', 'message': str(e)}), 500

    # Tüm kullanıcı kartlarını sil (placeholder - sadece başarılı döner)
    @app.route('/api/format_cards', methods=['POST'])
    def api_format_cards():
        try:
            if not session.get('logged_in'):
                return jsonify({'status': 'error', 'message': 'Unauthorized'}), 401
            # Sadece başarı döndür (cihazla işlem yok, DB'de kart tablosu yok)
            return jsonify({'status': 'success'})
        except Exception as e:
            logger.error(f"/api/format_cards error: {e}")
            return jsonify({'status': 'error', 'message': str(e)}), 500

    # Tüm kullanıcıları DB'den sil (mevcut kullanıcı hariç)
    @app.route('/api/delete_all_users', methods=['POST'])
    def api_delete_all_users():
        try:
            if not session.get('logged_in'):
                return jsonify({'status': 'error', 'message': 'Unauthorized'}), 401
            current_user_id = session.get('user_id')
            # users.db tarafı temizlik: mevcut kullanıcı HARİÇ herkes
            uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
            ucur = uconn.cursor()
            # Silinecek kullanıcı id'lerini al
            ids = [r['id'] for r in ucur.execute('SELECT id FROM users WHERE id != ?', (current_user_id,)).fetchall()]
            if ids:
                marks = ','.join(['?'] * len(ids))
                for table in ['user_access_logs','user_devices','user_device_access']:
                    try:
                        ucur.execute(f'DELETE FROM {table} WHERE user_id IN ({marks})', ids)
                    except Exception:
                        pass
                # Yerel avatarları sil
                try:
                    import os
                    for row in ucur.execute(f'SELECT profile_image FROM users WHERE id IN ({marks})', ids).fetchall():
                        pi = row['profile_image']
                        if pi and not str(pi).startswith('@'):
                            p = os.path.join('static','uploads',pi)
                            if os.path.exists(p):
                                os.remove(p)
                except Exception:
                    pass
                ucur.execute(f'DELETE FROM users WHERE id IN ({marks})', ids)
                uconn.commit()
            uconn.close()
            # data.db aynası: aynı id'leri sil
            try:
                dconn = get_db_connection(); dconn.row_factory = sqlite3.Row
                if ids:
                    dmarks = ','.join(['?'] * len(ids))
                    dconn.execute(f'DELETE FROM users WHERE id IN ({dmarks})', ids)
                    dconn.commit()
                dconn.close()
            except Exception:
                pass
            # 5 sn'lik MASTERCARD bastırma (cihaz getdeviceinfo ile hemen yeniden oluşturmaması için)
            try:
                from tcp_manager import tcp_manager
                tcp_manager.suppress_mastercard(5)
            except Exception:
                pass
            return jsonify({'status': 'success', 'deleted': len(ids) if ids else 0})
        except Exception as e:
            logger.error(f"/api/delete_all_users error: {e}")
            return jsonify({'status': 'error', 'message': str(e)}), 500

    # Mobile access routes
    @app.route('/mobile/login', methods=['GET', 'POST'])
    def mobile_login():
        import sqlite3
        if request.method == 'GET':
            return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'))
        # POST
        user_uuid = request.form.get('user_uuid', '').strip()
        password = request.form.get('password', '').strip()
        if not user_uuid:
            err = _('user_uuid_missing')
            flash(err, 'danger')
            return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
        conn = sqlite3.connect('users.db')
        conn.row_factory = sqlite3.Row
        user = conn.execute('SELECT id, full_name, is_active, password_hash, time_limit_start, time_limit_end FROM users WHERE user_uuid = ?', (user_uuid,)).fetchone()
        conn.close()
        if not user:
            err = _('user_not_found')
            flash(err, 'danger')
            return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
        if not user['is_active']:
            err = _('user_inactive')
            flash(err, 'danger')
            return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
        # Zaman limiti kontrolü (opsiyonel alanlar)
        try:
            tls = user.get('time_limit_start') if hasattr(user, 'get') else user['time_limit_start'] if 'time_limit_start' in user.keys() else None
            tle = user.get('time_limit_end') if hasattr(user, 'get') else user['time_limit_end'] if 'time_limit_end' in user.keys() else None
        except Exception:
            tls = None
            tle = None
        if tls or tle:
            try:
                from datetime import datetime
                now = datetime.now()
                fmt = '%Y-%m-%dT%H:%M'
                if tls:
                    start_ok = now >= datetime.strptime(tls[:16], fmt)
                    if not start_ok:
                        err = _('time_limit_not_started')
                        flash(err, 'danger')
                        return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
                if tle:
                    end_ok = now <= datetime.strptime(tle[:16], fmt)
                    if not end_ok:
                        err = _('time_limit_expired')
                        flash(err, 'danger')
                        return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
            except Exception:
                pass
        # Şifre doğrulama
        if not user['password_hash'] or user['password_hash'] == 'no_login_required' or not check_password_hash(user['password_hash'], password):
            err = _('wrong_password')
            flash(err, 'danger')
            return render_template('mobile_login.html', translations=TRANSLATIONS, current_language=session.get('dil','tr'), error_msg=err)
        session['mobile_logged_in'] = True
        session['mobile_user_id'] = int(user['id'])
        session['mobile_user_name'] = user['full_name'] or 'Mobil Kullanıcı'
        # Kullanıcı türünü oturuma yaz (butonları koşullamak için)
        try:
            # user_type'ı çek
            conn = sqlite3.connect('users.db')
            conn.row_factory = sqlite3.Row
            u2 = conn.execute('SELECT user_type FROM users WHERE id = ?', (int(user['id']),)).fetchone()
            conn.close()
            session['mobile_user_type'] = (u2['user_type'] if u2 else 'standard')
        except Exception:
            session['mobile_user_type'] = 'standard'
        return redirect(url_for('mobile_dashboard'))
    
    def _get_assigned_device_ids_for_user(user_id: int):
        import sqlite3
        try:
            conn = sqlite3.connect('users.db')
            conn.row_factory = sqlite3.Row
            rows = conn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
            conn.close()
            raw_ids = [str(r['device_id']) for r in rows]
            if not raw_ids:
                return []
            numeric_ids = [rid for rid in raw_ids if rid.isdigit()]
            serials = [rid for rid in raw_ids if not rid.isdigit()]
            mapped_ids = list(numeric_ids)
            if serials:
                try:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    # 1) Önce tam eşleşme
                    placeholders = ','.join(['?'] * len(serials))
                    exact_rows = dconn.execute(f'SELECT id, seri_no FROM cihazlar WHERE seri_no IN ({placeholders})', serials).fetchall()
                    matched_serials = set()
                    for r in exact_rows:
                        mapped_ids.append(str(r['id']))
                        matched_serials.add(str(r['seri_no']))
                    # 2) Tam eşleşmeyenler için prefix eşleşmesi (LIKE 'prefix%')
                    leftovers = [s for s in serials if s not in matched_serials]
                    for s in leftovers:
                        try:
                            like_rows = dconn.execute('SELECT id FROM cihazlar WHERE seri_no LIKE ? LIMIT 5', (s + '%',)).fetchall()
                            for lr in like_rows:
                                mapped_ids.append(str(lr['id']))
                        except Exception:
                            continue
                    dconn.close()
                except Exception:
                    pass
            # Tekrarlara karşı set, string döndür
            return list({str(x) for x in mapped_ids})
        except Exception:
            return []
    
    @app.route('/mobile/dashboard')
    def mobile_dashboard():
        if not session.get('mobile_logged_in'):
            return redirect(url_for('mobile_login'))
        user_id = session.get('mobile_user_id')
        # Kullanıcı türünü her istekte DB'den oku (login'e bağlı kalma)
        current_user_type = 'standard'
        user_profile_image_url = ''
        try:
            conn = sqlite3.connect('users.db'); conn.row_factory = sqlite3.Row
            row = conn.execute('SELECT user_type, full_name, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
            conn.close()
            if row and row['user_type']:
                current_user_type = row['user_type']
            # İsim de güncel kalsın
            if row and row['full_name']:
                session['mobile_user_name'] = row['full_name']
            # Profil resmi URL'si
            try:
                profile_image = row['profile_image'] if row else ''
                if profile_image:
                    imgfile = profile_image[1:] if str(profile_image).startswith('@') else profile_image
                    user_profile_image_url = url_for('static', filename='uploads/' + imgfile)
            except Exception:
                user_profile_image_url = ''
        except Exception:
            pass
        device_ids = _get_assigned_device_ids_for_user(user_id)
        # Cihaz bilgilerini ana db'den al
        devices = []
        try:
            conn = sqlite3.connect('data.db')
            conn.row_factory = sqlite3.Row
            if device_ids:
                placeholders = ','.join(['?'] * len(device_ids))
                query = f'SELECT id, ad, seri_no, tcp_tipi, ip_adresi FROM cihazlar WHERE id IN ({placeholders})'
                rows = conn.execute(query, device_ids).fetchall()
            else:
                rows = []
            conn.close()
            for r in rows:
                dev_id = str(r['id'])
                devices.append({
                    'serial_number': dev_id,
                    'device_name': r['ad'],
                    'ip_address': r['ip_adresi'],
                    'device_type': r['tcp_tipi'],
                    'is_connected': tcp_manager.is_device_connected(dev_id) if 'tcp_manager' in globals() else False
                })
        except Exception:
            devices = []
        return render_template('mobile_dashboard.html', user_name=session.get('mobile_user_name', 'Mobil Kullanıcı'), user_type=current_user_type, devices=devices, user_profile_image_url=user_profile_image_url, translations=TRANSLATIONS, current_language=session.get('dil','tr'))
    
    @app.route('/mobile/api/device-status')
    def mobile_device_status():
        if not session.get('mobile_logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        user_id = session.get('mobile_user_id')
        device_ids = _get_assigned_device_ids_for_user(user_id)
        status_list = []
        for did in device_ids:
            status_list.append({'id': did, 'connected': tcp_manager.is_device_connected(did) if 'tcp_manager' in globals() else False})
        return jsonify({'success': True, 'devices': status_list})
    
    @app.route('/mobile/api/open-device', methods=['POST'])
    def mobile_open_device():
        if not session.get('mobile_logged_in'):
            return jsonify({'success': False, 'message': _('Unauthorized')}), 401
        data = request.get_json(silent=True) or {}
        device_id = str(data.get('device_id', '')).strip()
        action = str(data.get('action', '')).strip()
        try:
            logger.info(f"mobile_open_device: device_id={device_id}, action={action}")
        except Exception:
            pass
        # Normalize provided device identifier
        provided_serial = None
        numeric_device_id = None
        if device_id.isdigit():
            numeric_device_id = device_id
        else:
            provided_serial = device_id
            try:
                dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                row_exact = dconn.execute('SELECT id, seri_no FROM cihazlar WHERE seri_no = ? LIMIT 1', (provided_serial,)).fetchone()
                if row_exact:
                    numeric_device_id = str(row_exact['id'])
                    provided_serial = str(row_exact['seri_no'])
                else:
                    row_like = dconn.execute('SELECT id, seri_no FROM cihazlar WHERE seri_no LIKE ? LIMIT 1', (provided_serial + '%',)).fetchone()
                    if row_like:
                        numeric_device_id = str(row_like['id'])
                        provided_serial = str(row_like['seri_no'])
                dconn.close()
            except Exception:
                numeric_device_id = None
        # Kullanıcı pasif mi? Pasifse erişim izni verme
        try:
            _conn_chk = sqlite3.connect('users.db'); _conn_chk.row_factory = sqlite3.Row
            _u = _conn_chk.execute('SELECT is_active FROM users WHERE id = ?', (session.get('mobile_user_id'),)).fetchone()
            _conn_chk.close()
            if (not _u) or int(_u['is_active']) != 1:
                return jsonify({'success': False, 'message': _('user_inactive')}), 403
        except Exception:
            # Her ihtimale karşı, kullanıcı durumu okunamazsa erişimi engelle
            return jsonify({'success': False, 'message': _('user_status_unverified')}), 403
        # Zaman limiti kontrolü (varsa)
        try:
            _conn_tl = sqlite3.connect('users.db'); _conn_tl.row_factory = sqlite3.Row
            _tl = _conn_tl.execute('SELECT time_limit_start, time_limit_end FROM users WHERE id = ?', (session.get('mobile_user_id'),)).fetchone()
            _conn_tl.close()
            if _tl:
                tls = _tl['time_limit_start']
                tle = _tl['time_limit_end']
                if tls or tle:
                    from datetime import datetime as _dt
                    now = _dt.now()
                    fmt = '%Y-%m-%dT%H:%M'
                    if tls:
                        try:
                            if now < _dt.strptime(str(tls)[:16], fmt):
                                return jsonify({'success': False, 'message': _('time_limit_not_started')}), 403
                        except Exception:
                            pass
                    if tle:
                        try:
                            if now > _dt.strptime(str(tle)[:16], fmt):
                                return jsonify({'success': False, 'message': _('time_limit_expired')}), 403
                        except Exception:
                            pass
        except Exception:
            pass
        # Kart/UUID: mobil kullanıcı kendi user_uuid'si ile grantaccess komutu atacak
        user_uuid = None
        try:
            conn = sqlite3.connect('users.db')
            conn.row_factory = sqlite3.Row
            u = conn.execute('SELECT user_uuid FROM users WHERE id = ?', (session.get('mobile_user_id'),)).fetchone()
            conn.close()
            user_uuid = (u['user_uuid'] if u else None) or ''
        except Exception:
            user_uuid = ''
        if not device_id:
            return jsonify({'success': False, 'message': _('invalid_device')}), 400
        user_id = session.get('mobile_user_id')
        assigned_ids = set(_get_assigned_device_ids_for_user(user_id) or [])
        has_access = False
        device_full_serial = None
        try:
            if numeric_device_id:
                dconn2 = sqlite3.connect('data.db'); dconn2.row_factory = sqlite3.Row
                dr = dconn2.execute('SELECT seri_no FROM cihazlar WHERE id = ?', (numeric_device_id,)).fetchone()
                dconn2.close()
                if dr and dr['seri_no']:
                    device_full_serial = str(dr['seri_no']).strip()
            elif provided_serial:
                device_full_serial = provided_serial
        except Exception:
            device_full_serial = None
        if numeric_device_id and numeric_device_id in assigned_ids:
            has_access = True
        if not has_access:
            try:
                # Fallback: check raw assignments against this device's serial/ID
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                raw_rows = uconn.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)).fetchall()
                uconn.close()
                raw_values = [str(r['device_id']).strip() for r in raw_rows]
                # Get device serial for given numeric id
                dev_serial = None
                try:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    drow = dconn.execute('SELECT seri_no FROM cihazlar WHERE id = ?', (device_id,)).fetchone()
                    dconn.close()
                    if drow and drow['seri_no']:
                        dev_serial = str(drow['seri_no']).strip()
                except Exception:
                    dev_serial = None
                for rv in raw_values:
                    if rv.isdigit():
                        if numeric_device_id and rv == numeric_device_id:
                            has_access = True
                            break
                    else:
                        # Prefix or exact match against the device's full serial
                        if device_full_serial and device_full_serial.startswith(rv):
                            has_access = True
                            break
                        if provided_serial and (provided_serial == rv or rv.startswith(provided_serial)):
                            has_access = True
                            break
            except Exception:
                has_access = False
        if not has_access:
            return jsonify({'success': False, 'message': _('no_permission_for_device')}), 403
        # Cihaz bağlı mı?
        try:
            check_id = numeric_device_id or device_id
            connected = tcp_manager.is_device_connected(check_id) if 'tcp_manager' in globals() else False
        except Exception:
            connected = False
        if not connected:
            return jsonify({'success': False, 'message': _('device_not_connected')}), 503
        # Komut gönder: device_command ile aynı JSON yapısı {"grantaccess": "<uuid>"}
        if not user_uuid:
            return jsonify({'success': False, 'message': _('user_uuid_missing')}), 400
        # Cihazdan yanıt beklemeden user_access_logs'a anında kayıt (istemci isteği üzerine)
        try:
            utype = 'standard'
            person_name = 'Mobil Kullanıcı'
            from datetime import datetime as _dt; ts_now = _dt.now().strftime('%d-%m-%y %H:%M:%S')
            connu = sqlite3.connect('users.db')
            connu.row_factory = sqlite3.Row
            rowu = connu.execute('SELECT full_name, user_type FROM users WHERE id = ?', (user_id,)).fetchone()
            connu.close()
            if rowu:
                if rowu['user_type']:
                    utype = rowu['user_type']
                if rowu['full_name']:
                    person_name = rowu['full_name']
            action_map = {
                'standard_open': _('mobile_app_entry'),
                'shift_in': _('shift_in_label'),
                'shift_out': _('shift_out_label'),
                'break_in': _('break_in_label'),
                'break_out': _('break_out_label'),
                'guest_in': _('guest_in_label'),
                'guest_out': _('guest_out_label')
            }
            description = action_map.get(action or 'standard_open', _('mobile_app_entry'))
            region = device_full_serial or (numeric_device_id or device_id)
            device_row_id = None
            try:
                dconn = sqlite3.connect('data.db')
                dconn.row_factory = sqlite3.Row
                drow = dconn.execute('SELECT id, device_name FROM user_devices WHERE serial_number = ? AND user_id = ?', (region, user_id)).fetchone()
                dconn.close()
                if drow and drow['device_name']:
                    region = drow['device_name']
                if drow and drow['id']:
                    device_row_id = drow['id']
            except Exception:
                pass
            try:
                user_manager.last_access_time = ts_now
            except Exception:
                pass
            user_manager.add_access_log(user_id=user_id, device_id=(device_row_id if device_row_id is not None else None), person_name=person_name, region=region, access_type=utype, notes=description, access_status='granted')
        except Exception as _ie:
            logger.error(f"instant user_access_logs insert hata: {_ie}")
        try:
            import json as _json
            command_string = _json.dumps({'grantaccess': user_uuid})
            ok = tcp_manager.send_command_to_device(device_id, command_string)
            if not ok:
                return jsonify({'success': False, 'message': 'Komut gönderilemedi'}), 500
            # Cihaz yanıtını bekle (device_command ile aynı mantık)
            resp = tcp_manager.get_command_response(device_id, timeout=15)
            if resp:
                if resp.get('status') == 'success':
                    # Başarılı: kullanıcı bazlı geçiş kaydı ekle
                    try:
                        # user_type'ı al
                        utype = 'standard'
                        connu = sqlite3.connect('users.db')
                        connu.row_factory = sqlite3.Row
                        rowu = connu.execute('SELECT full_name, user_type FROM users WHERE id = ?', (user_id,)).fetchone()
                        connu.close()
                        if rowu and rowu['user_type']:
                            utype = rowu['user_type']
                        person_name = (rowu['full_name'] if rowu and rowu['full_name'] else 'Mobil Kullanıcı')
                        # action->açıklama
                        action_map = {
                            'standard_open': _('mobile_app_entry'),
                            'shift_in': _('shift_in_label'),
                            'shift_out': _('shift_out_label'),
                            'break_in': _('break_in_label'),
                            'break_out': _('break_out_label'),
                            'guest_in': _('guest_in_label'),
                            'guest_out': _('guest_out_label')
                        }
                        description = action_map.get(action or 'standard_open', _('mobile_app_entry'))
                        # device name bölge bilgisi
                        region = device_full_serial or (numeric_device_id or device_id)
                        device_row_id = None
                        try:
                            dconn = sqlite3.connect('data.db')
                            dconn.row_factory = sqlite3.Row
                            drow = dconn.execute('SELECT id, device_name FROM user_devices WHERE serial_number = ? AND user_id = ?', (region, user_id)).fetchone()
                            dconn.close()
                            if drow and drow['device_name']:
                                region = drow['device_name']
                            if drow and drow['id']:
                                device_row_id = drow['id']
                        except Exception:
                            pass
                        # user_access_logs ekle
                        try:
                            try:
                                user_manager.last_access_time = ts_now
                            except Exception:
                                pass
                            user_manager.add_access_log(user_id=user_id, device_id=(device_row_id if device_row_id is not None else None), person_name=person_name, region=region, access_type=utype, notes=description, access_status='granted')
                        except Exception as _e:
                            logger.error(f"user_access_logs ekleme hatası: {_e}")
                        # Ayrıca merkezi access_logs'a da kayıt ekle (mobil onaylandı) + notu ayrı eşlemede tut
                        try:
                            conn_main = get_db_connection()
                            # access_logs(device_id, access_type, card_uuid, is_terminal, is_granted, timestamp)
                            cur = conn_main.cursor()
                            cur.execute('''
                                INSERT INTO access_logs (device_id, access_type, card_uuid, is_terminal, is_granted, timestamp)
                                VALUES (?, ?, ?, ?, ?, ?)
                            ''', (numeric_device_id or device_id, 3, user_uuid, 2, 1, ts_now))
                            access_log_id = cur.lastrowid
                            try:
                                cur.execute('CREATE TABLE IF NOT EXISTS mobile_access_notes (access_log_id INTEGER PRIMARY KEY, note TEXT)')
                                cur.execute('INSERT OR REPLACE INTO mobile_access_notes (access_log_id, note) VALUES (?, ?)', (access_log_id, description))
                            except Exception:
                                pass
                            conn_main.commit()
                            conn_main.close()
                        except Exception as _e2:
                            logger.error(f"access_logs ekleme (mobil) hatası: {_e2}")
                    except Exception:
                        pass
                    return jsonify({'success': True})
                else:
                    return jsonify({'success': False, 'message': 'Cihaz reddetti'}), 403
            else:
                return jsonify({'success': False, 'message': 'Cihaz yanıt vermedi'}), 504
        except Exception as e:
            logger.error(f"open-device hata: {e}")
            return jsonify({'success': False, 'message': _('command_error')}), 500
    
    @app.route('/mobile/device-logs/<device_id>')
    def mobile_device_logs(device_id):
        if not session.get('mobile_logged_in'):
            return redirect(url_for('mobile_login'))
        # Şimdilik loglar boş
        return render_template('mobile_device_logs.html', device_name=device_id, logs=[])
    
    @app.route('/mobile/logout')
    def mobile_logout():
        session.pop('mobile_logged_in', None)
        session.pop('mobile_user_id', None)
        session.pop('mobile_user_name', None)
        return redirect(url_for('mobile_login'))

    # Basit güncelleme kontrol ucu (şimdilik hep true döndürür)
    @app.route('/api/check-updates')
    def api_check_updates():
        try:
            last_id = int(request.args.get('last_id', 0))
        except Exception:
            last_id = 0
        try:
            conn = get_db_connection()
            row = conn.execute('SELECT MAX(id) as max_id FROM access_logs').fetchone()
            conn.close()
            max_id = int(row['max_id']) if row and row['max_id'] is not None else 0
            return jsonify({'has_updates': (max_id > last_id), 'latest_id': max_id})
        except Exception as e:
            logger.error(f"/api/check-updates hata: {e}")
            return jsonify({'has_updates': False, 'latest_id': last_id})

    @app.route('/api/admin/open-device', methods=['POST'])
    def api_admin_open_device():
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            data = request.get_json(silent=True) or {}
            device_id = str(data.get('device_id', '')).strip()
            user_id = session.get('user_id')
            # Kullanıcı UUID'si
            user_uuid = (session.get('user_uuid') or '')
            # 1) data.db -> users.id = session user_id
            if not user_uuid:
                try:
                    d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                    ur = d1.execute('SELECT user_uuid FROM users WHERE id = ?', (user_id,)).fetchone()
                    d1.close()
                    if ur and ur['user_uuid']:
                        user_uuid = ur['user_uuid']
                except Exception:
                    pass
            # 2) users.db -> users.id = session user_id
            if not user_uuid:
                try:
                    conn = sqlite3.connect('users.db'); conn.row_factory = sqlite3.Row
                    u = conn.execute('SELECT user_uuid FROM users WHERE id = ?', (user_id,)).fetchone()
                    conn.close()
                    if u and u['user_uuid']:
                        user_uuid = u['user_uuid']
                except Exception:
                    pass
            if not device_id:
                return jsonify({'success': False, 'message': 'Geçersiz cihaz'}), 400
            # access_logs'a bir kayıt ekle (access_type=3 TCP SERVER), is_terminal=2 (admin panel), is_granted=1
            from datetime import datetime as _dt; ts_now = _dt.now().strftime('%d-%m-%y %H:%M:%S')
            try:
                conn_main = get_db_connection()
                cur = conn_main.cursor()
                cur.execute('''
                    INSERT INTO access_logs (device_id, access_type, card_uuid, is_terminal, is_granted, timestamp)
                    VALUES (?, ?, ?, ?, ?, ?)
                ''', (device_id, 3, (user_uuid or ''), 2, 1, ts_now))
                access_log_id = cur.lastrowid
                # Not haritasına 'Admin Panel' notu ekle
                try:
                    cur.execute('CREATE TABLE IF NOT EXISTS mobile_access_notes (access_log_id INTEGER PRIMARY KEY, note TEXT)')
                    cur.execute('INSERT OR REPLACE INTO mobile_access_notes (access_log_id, note) VALUES (?, ?)', (access_log_id, 'Admin Panel'))
                except Exception:
                    pass
                conn_main.commit(); conn_main.close()
            except Exception as e:
                logger.error(f"Admin open-device log insert hata: {e}")
                return jsonify({'success': False, 'message': 'Log yazılamadı'}), 500
            # Cihaza komutu da gönder (sessiz)
            try:
                import json as _json
                if not user_uuid:
                    logger.error(f"Admin open-device için user_uuid bulunamadı: user_id={user_id}")
                    return jsonify({'success': False, 'message': 'Kullanıcı UUID bulunamadı'}), 400
                command_string = _json.dumps({'grantaccess': user_uuid})
                logger.info(f"GrantAccess komutu gönderiliyor: device={device_id}, user_uuid={user_uuid}")
                tcp_manager.send_command_to_device(device_id, command_string)
            except Exception:
                pass
            return jsonify({'success': True})
        except Exception as e:
            logger.error(f"api_admin_open_device hata: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    def _prepare_access_logs_common(device_serials, page, per_page, query_text):
        try:
            conn = get_db_connection()
            base_sql = 'SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp FROM access_logs'
            params = []
            if device_serials is not None:
                # Eğer çağıran non-admin ve hiç cihazı yoksa: boş dön
                if isinstance(device_serials, list) and len(device_serials) == 0:
                    return [], 0, page, per_page, 1
                if len(device_serials) > 0:
                    marks = ','.join(['?'] * len(device_serials))
                    base_sql += f' WHERE device_id IN ({marks})'
                    params.extend([str(s) for s in device_serials])
            base_sql += ' ORDER BY timestamp DESC'
            rows = conn.execute(base_sql, params).fetchall()
            device_rows = conn.execute('SELECT id, ad, seri_no FROM cihazlar').fetchall()
            conn.close()

            logs = []
            for r in rows:
                logs.append({
                    'id': r['id'],
                    'device_id': str(r['device_id']),
                    'user_name': '',
                    'device_name': '',
                    'card_uuid': r['card_uuid'],
                    'access_type': r['access_type'],
                    'is_granted': r['is_granted'],
                    'timestamp': r['timestamp'],
                    'notes': ('Cihaz Verisi' if (str(r['is_terminal']) != '2') else ''),
                    'is_terminal': r['is_terminal'],
                    'source': 'central'
                })

            # Enrichment (aynı kullanıcıya ait isim/cihaz adlarıyla sınırlı kalır)
            try:
                try:
                    all_user_devices = user_manager.get_all_user_devices()
                    user_device_name_map = {str(d['serial_number']): (d.get('device_name') or '') for d in all_user_devices if d.get('serial_number')}
                except Exception:
                    user_device_name_map = {}
                device_name_map = {str(r['seri_no']): r['ad'] for r in device_rows if r['ad']}

                uuid_to_name = {}
                try:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    for ur in dconn.execute('SELECT id, user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid']:
                            uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                    dconn.close()
                except Exception:
                    pass
                try:
                    uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                    for ur in uconn.execute('SELECT id, user_uuid, full_name FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid'] and str(ur['user_uuid']) not in uuid_to_name:
                            uuid_to_name[str(ur['user_uuid'])] = ur['full_name'] or ''
                        uconn.close()
                except Exception:
                    pass

                uuid_to_user_id = {}
                try:
                    uconn3 = sqlite3.connect('users.db'); uconn3.row_factory = sqlite3.Row
                    for ur in uconn3.execute('SELECT id, user_uuid FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid']:
                            uuid_to_user_id[str(ur['user_uuid'])] = ur['id']
                    uconn3.close()
                except Exception:
                    pass
                try:
                    dconn3 = sqlite3.connect('data.db'); dconn3.row_factory = sqlite3.Row
                    for ur in dconn3.execute('SELECT id, user_uuid FROM users WHERE user_uuid IS NOT NULL'):
                        if ur['user_uuid'] and str(ur['user_uuid']) not in uuid_to_user_id:
                            uuid_to_user_id[str(ur['user_uuid'])] = ur['id']
                    dconn3.close()
                except Exception:
                    pass

                for item in logs:
                    serial = item['device_id']
                    item['device_name'] = user_device_name_map.get(serial) or device_name_map.get(serial) or ''
                    item['user_name'] = uuid_to_name.get(str(item['card_uuid']).strip(), '')
                    try:
                        cu = str(item.get('card_uuid') or '').strip().upper()
                        if cu in ('BUTTON-1','BUTTON-2'):
                            base = item['device_name'] or str(item['device_id'])
                            item['user_name'] = f"{base} BUTTON"
                    except Exception:
                        pass
                    try:
                        if str(item.get('access_type')) == '3' and str(item.get('is_terminal','0')) == '2':
                            note_filled = False
                            try:
                                _m = sqlite3.connect('data.db'); _m.row_factory = sqlite3.Row
                                _mr = _m.execute('SELECT note FROM mobile_access_notes WHERE access_log_id = ?', (item['id'],)).fetchone()
                                _m.close()
                                if _mr and _mr['note']:
                                    item['notes'] = _mr['note']
                                    note_filled = True
                            except Exception:
                                pass
                            if not note_filled:
                                try:
                                    uid = uuid_to_user_id.get(str(item.get('card_uuid')))
                                    if uid:
                                        _c = sqlite3.connect('data.db'); _c.row_factory = sqlite3.Row
                                        try:
                                            _r = _c.execute('SELECT notes FROM user_access_logs WHERE user_id = ? AND access_time = ? ORDER BY access_time DESC LIMIT 1', (uid, item.get('timestamp'))).fetchone()
                                        except Exception:
                                            _r = _c.execute('SELECT notes FROM user_access_logs WHERE user_id = ? ORDER BY access_time DESC LIMIT 1', (uid,)).fetchone()
                                        _c.close()
                                        if _r and _r['notes']:
                                            item['notes'] = _r['notes']
                                except Exception:
                                    pass
                    except Exception:
                        pass
            except Exception as enrich_err:
                logger.warning(f"_prepare_access_logs_common enrich warn: {enrich_err}")

            total_logs = len(logs)
            q = (query_text or '').strip().lower()
            if q:
                filtered = []
                for g in logs:
                    type_tokens = []
                    try:
                        at = str(g.get('access_type','')).strip(); it = str(g.get('is_terminal','')).strip()
                        if at in ['1','nfc']: type_tokens += ['nfc']
                        elif at in ['2','button','buton']: type_tokens += ['button','buton']
                        elif at in ['3']:
                            if it == '2': type_tokens += ['mobil sayfa','mobil','tcp server']
                            else: type_tokens += ['tcp server']
                        elif at in ['4']: type_tokens += ['tcp client']
                        elif at in ['5']: type_tokens += ['ble','bluetooth']
                        elif at in ['6']: type_tokens += ['rs485','rs-485']
                    except Exception:
                        pass
                    status_text = 'izin verildi' if g.get('is_granted') else 'izin verilmedi'
                    haystack = ' '.join([
                        str(g.get('user_name','')),
                        str(g.get('card_uuid','')),
                        str(g.get('device_name','')),
                        str(g.get('device_id','')),
                        str(g.get('notes','')),
                        ' '.join(type_tokens),
                        status_text,
                        str(g.get('timestamp',''))
                    ]).lower()
                    if q in haystack:
                        filtered.append(g)
                logs = filtered
                total_logs = len(logs)

            # Son garanti: user_name boş olan ve card_uuid dolu kayıtlar için toplu UUID->ad çözümleme (card_uuid üzerinden, hex/dec normalize)
            try:
                # Kullanıcılardan card_uuid -> full_name haritası oluştur (users.db + data.db)
                user_map = {}
                def add_forms(uval, name):
                    s = str(uval or '').strip().lower()
                    if not s or s == 'mastercard':
                        return
                    try:
                        # ham anahtar
                        if s not in user_map:
                            user_map[s] = name or ''
                        if s.startswith('0x'):
                            n = int(s, 16)
                            decs = str(n)
                            hx = '0x' + format(n, 'x')
                            hx8 = '0x' + format(n, '08x')
                            for k in (decs, hx, hx8):
                                if k not in user_map:
                                    user_map[k] = name or ''
                        elif s.isdigit():
                            n = int(s, 10)
                            decs = str(n)
                            hx = '0x' + format(n, 'x')
                            hx8 = '0x' + format(n, '08x')
                            for k in (decs, hx, hx8):
                                if k not in user_map:
                                    user_map[k] = name or ''
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', s):
                                n = int(s, 16)
                                decs = str(n)
                                hx = '0x' + format(n, 'x')
                                hx8 = '0x' + format(n, '08x')
                                preserved = '0x' + s
                                for k in (preserved, decs, hx, hx8):
                                    if k not in user_map:
                                        user_map[k] = name or ''
                    except Exception:
                        pass
                    try:
                        u1 = sqlite3.connect('users.db'); u1.row_factory = sqlite3.Row
                        for ur in u1.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                            add_forms(ur['card_uuid'], ur['full_name'])
                            u1.close()
                    except Exception:
                        pass
                    try:
                        d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                        for ur in d1.execute('SELECT full_name, card_uuid FROM users WHERE card_uuid IS NOT NULL AND TRIM(card_uuid)<>""'):
                            add_forms(ur['card_uuid'], ur['full_name'])
                            d1.close()
                    except Exception:
                        pass
                # Loglarda adı boş olanlara eşleştirme uygula
                    for it in logs:
                        try:
                            if it.get('card_uuid') and not it.get('user_name'):
                                key = str(it.get('card_uuid')).strip().lower()
                                name = None
                                # Aday anahtarlar
                                candidates = [key]
                                try:
                                    if key.startswith('0x'):
                                        n = int(key, 16)
                                        candidates += [str(n), '0x'+format(n,'x'), '0x'+format(n,'08x')]
                                    elif key.isdigit():
                                        n = int(key, 10)
                                        candidates += [str(n), '0x'+format(n,'x'), '0x'+format(n,'08x')]
                                    else:
                                        import re
                                        if re.fullmatch(r'[0-9a-f]+', key):
                                            n = int(key, 16)
                                            candidates += ['0x'+key, '0x'+format(n,'x'), '0x'+format(n,'08x'), str(n)]
                                except Exception:
                                    pass
                                for ckey in candidates:
                                    if ckey in user_map:
                                        name = user_map[ckey]
                                        break
                                if name:
                                    it['user_name'] = name
                        except Exception:
                            continue
            except Exception:
                pass

            total_pages = (total_logs + per_page - 1)//per_page if total_logs>0 else 1
            start_idx = max(0, (page-1)*per_page); end_idx = start_idx + per_page
            page_logs = logs[start_idx:end_idx]
            return page_logs, total_logs, page, per_page, total_pages
        except Exception as e:
            logger.error(f"_prepare_access_logs_common error: {e}")
            return [], 0, page, per_page, 1

    @app.route('/access-logs')
    def access_logs():
        """Access logs sayfası"""
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        # Sayfalama parametreleri
        try:
            page = int(request.args.get('page', 1))
        except Exception:
            page = 1
        try:
            per_page = int(request.args.get('per_page', 50))
        except Exception:
            per_page = 50
        if per_page > 50:
            per_page = 50
        try:
            is_admin = session.get('is_admin', False)
            current_user_id = session.get('user_id')
            device_serials = None
            if not is_admin:
                # Non-admin: yalnızca kendi cihazlarına ait loglar
                try:
                    dtmp = get_db_connection(); dtmp.row_factory = sqlite3.Row
                    dev_rows = dtmp.execute('SELECT id FROM cihazlar WHERE user_id = ?', (current_user_id,)).fetchall()
                    dtmp.close()
                    device_serials = [str(r['id']) for r in dev_rows]
                except Exception:
                    device_serials = []
            logs, total_logs, page, per_page, total_pages = _prepare_access_logs_common(device_serials, page, per_page, request.args.get('adsoyad',''))
            # Navbar bilgileri
            try:
                cu = None
                if current_user_id:
                    dconn = sqlite3.connect('data.db'); dconn.row_factory = sqlite3.Row
                    drow = dconn.execute('SELECT id, full_name, email, company, profile_image FROM users WHERE id = ?', (current_user_id,)).fetchone()
                    dconn.close()
                    cu = drow
            except Exception:
                cu = None
            return render_template('access_logs.html', logs=logs, total_logs=total_logs, page=page, per_page=per_page, total_pages=total_pages, get_page_range=lambda current, total: range(1, min(total, 20)+1), user=cu, current_user=cu, user_name=(cu['full_name'] if cu else session.get('user_name')), translations=TRANSLATIONS, current_language=session.get('dil','tr'), static_bust=int(time.time()))
        except Exception as e:
            logger.error(f"access_logs hazırlanamadı: {e}")
            return render_template('access_logs.html', logs=[], total_logs=0, page=1, per_page=50, total_pages=1, translations=TRANSLATIONS, current_language=session.get('dil','tr'))

    @app.route('/mobile/api/user-type')
    def mobile_user_type():
        if not session.get('mobile_logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            uid = session.get('mobile_user_id')
            conn = sqlite3.connect('users.db'); conn.row_factory = sqlite3.Row
            row = conn.execute('SELECT user_type FROM users WHERE id = ?', (uid,)).fetchone()
            conn.close()
            return jsonify({'success': True, 'user_type': (row['user_type'] if row and row['user_type'] else 'standard')})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/mobile/api/user-by-uuid')
    def mobile_user_by_uuid():
        try:
            user_uuid = (request.args.get('uuid', '') or '').strip()
            if not user_uuid:
                return jsonify({'success': False, 'message': 'uuid required'}), 400
            conn = sqlite3.connect('users.db'); conn.row_factory = sqlite3.Row
            row = conn.execute('SELECT full_name, profile_image FROM users WHERE user_uuid = ?', (user_uuid,)).fetchone()
            conn.close()
            if not row:
                return jsonify({'success': True, 'found': False})
            full_name = row['full_name'] or ''
            profile_image = row['profile_image'] or ''
            image_url = ''
            if profile_image:
                try:
                    imgfile = profile_image[1:] if profile_image.startswith('@') else profile_image
                    image_url = url_for('static', filename='uploads/' + imgfile)
                except Exception:
                    image_url = ''
            return jsonify({'success': True, 'found': True, 'full_name': full_name, 'image_url': image_url})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/device_readlog/<device_id>', methods=['POST'])
    def api_device_readlog(device_id):
        if 'user_id' not in session:
            return jsonify({'success': False, 'message': _('unauthorized')}), 401
        # Cihaz bağlı mı?
        try:
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': _('device_not_connected')}), 400
        except Exception:
            return jsonify({'success': False, 'message': _('device_not_connected')}), 400
        # Komutu gönder
        try:
            ok = tcp_manager.send_command_to_device(str(device_id), 'readlog')
            if not ok:
                return jsonify({'success': False, 'message': _('command_error')}), 500
            # 30 sn bekle (upload için geniş süre)
            resp = tcp_manager.get_command_response(str(device_id), timeout=30)
            if not resp or resp.get('status') != 'success':
                return jsonify({'success': False, 'message': resp.get('message') if resp else _('command_error')}), 500
            raw = resp.get('data', b'')
            # bytes -> utf-8 text
            try:
                text = raw.decode('utf-8', errors='ignore') if isinstance(raw, (bytes, bytearray)) else str(raw)
            except Exception:
                text = ''
            # Basit JSON parçalama: içindeki ardışık { } bloklarını yakala ve accessLog içerenleri topla
            import json as _json
            parsed_entries = []
            buf = text
            brace = 0
            start = None
            for idx, ch in enumerate(buf):
                if ch == '{':
                    if brace == 0:
                        start = idx
                    brace += 1
                elif ch == '}':
                    brace = max(0, brace - 1)
                    if brace == 0 and start is not None:
                        snippet = buf[start:idx+1]
                        try:
                            obj = _json.loads(snippet)
                            if isinstance(obj, dict) and 'accessLog' in obj:
                                items = obj.get('accessLog') or []
                                if isinstance(items, list):
                                    parsed_entries.extend(items)
                        except Exception:
                            pass
                        start = None
            return jsonify({'success': True, 'text': text, 'entries': parsed_entries})
        except Exception as e:
            logger.error(f"/api/device_readlog hata: {e}")
            return jsonify({'success': False, 'message': _('command_error')})

    @app.route('/api/device-logs/<device_id>/clear', methods=['POST'])
    def api_device_logs_clear(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            # clearlog komutunu ham şekilde gönder
            ok = tcp_manager.send_command_to_device(str(device_id), 'clearlog')
            if not ok:
                return jsonify({'success': True})
            # Yanıtı kısaca bekle ama sonucu önemsemeden success döneceğiz
            try:
                tcp_manager.get_command_response(str(device_id), timeout=5)
            except Exception:
                pass
            return jsonify({'success': True})
        except Exception:
            return jsonify({'success': True})

    @app.route('/device-logs/<device_id>/download')
    def device_logs_download(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        # Mevcut sayfadaki render mantığına benzer şekilde en güncel veriyi topla
        entries = []
        try:
            if tcp_manager.is_device_connected(str(device_id)):
                ok = tcp_manager.send_command_to_device(str(device_id), 'readlog')
                if ok:
                    resp = tcp_manager.get_command_response(str(device_id), timeout=30)
                    if resp and resp.get('status') == 'success':
                        raw = resp.get('data', b'')
                        try:
                            text = raw.decode('utf-8', errors='ignore') if isinstance(raw, (bytes, bytearray)) else str(raw)
                        except Exception:
                            text = ''
                        for obj in tcp_manager._iter_json_stream(text):
                            try:
                                if isinstance(obj, list):
                                    for it in obj:
                                        if isinstance(it, dict) and any(k in it for k in ['deviceid','uuid','type','isterminal','isgranted','timestamp','deviceId','UUID','Type']):
                                            entries.append(it)
                                    continue
                                if not isinstance(obj, dict):
                                    continue
                                items = None
                                if 'accessLog' in obj:
                                    items = obj.get('accessLog')
                                elif 'AccessLog' in obj:
                                    items = obj.get('AccessLog')
                                elif 'accesslog' in obj:
                                    items = obj.get('accesslog')
                                elif isinstance(obj.get('data'), dict):
                                    d = obj.get('data')
                                    items = d.get('accessLog') or d.get('AccessLog') or d.get('accesslog')
                                elif any(k in obj for k in ['deviceid','uuid','type','isterminal','isgranted','timestamp','deviceId','UUID','Type']):
                                    items = [obj]
                                if isinstance(items, list):
                                    entries.extend([e for e in items if isinstance(e, dict)])
                            except Exception:
                                continue
                        # Binary fallback
                        if not entries and isinstance(raw, (bytes, bytearray)) and len(raw) >= 40:
                            buf = bytes(raw)
                            rec_size = 40
                            for i in range(0, len(buf) - rec_size + 1, rec_size):
                                chunk = buf[i:i+rec_size]
                                access_type_val = chunk[0]
                                is_granted_val = chunk[1]
                                ts_bytes = chunk[2:27]
                                uuid_bytes = chunk[27:39]
                                term_mode_val = chunk[39]
                                def clean(bs):
                                    try:
                                        return bs.split(b'\x00', 1)[0].decode('utf-8', errors='ignore').strip().strip('\x00')
                                    except Exception:
                                        return ''
                                ts_str = clean(ts_bytes)
                                uuid_str = clean(uuid_bytes)
                                type_map = {1:'NFC', 2:'BUTTON', 3:'TCP SERVER', 4:'TCP CLIENT', 5:'BLE', 6:'RS485'}
                                type_label = type_map.get(int(access_type_val), 'UNKNOWN')
                                entries.append({'deviceid': device_id, 'type': type_label, 'uuid': uuid_str,
                                                'isterminal': True if int(term_mode_val) == 1 else False,
                                                'isgranted': True if int(is_granted_val) == 222 else False,
                                                'timestamp': ts_str})
        except Exception:
            pass
        # Eşleşme: UUID -> İsim
        try:
            # Cihaz adı lookup (isteğe bağlı)
            dev_name = None
            try:
                conn = get_db_connection()
                rr = conn.execute('SELECT ad FROM cihazlar WHERE id = ? OR seri_no = ?', (device_id, device_id)).fetchone()
                conn.close()
                if rr and rr['ad']:
                    dev_name = rr['ad']
            except Exception:
                dev_name = None
            mapped = []
            for e in entries:
                atype_str = (str((e.get('type') or e.get('Type') or '')).upper())
                atype_map = {'NFC':1, 'BUTTON':2, 'TCP SERVER':3, 'TCP CLIENT':4, 'BLE':5, 'RS485':6}
                isg = e.get('isgranted')
                if isinstance(isg, str):
                    isg = True if isg.lower() in ['222','true','yes'] else False
                uname = ''
                cu = (e.get('uuid') or e.get('UUID') or '').strip()
                if cu:
                    # normalize hex/dec and search users.card_uuid first
                    key = cu.strip().lower()
                    candidates = [key]
                    try:
                        if key.startswith('0x'):
                            n=int(key,16); candidates += [str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                        elif key.isdigit():
                            n=int(key,10); candidates += [str(n),'0x'+format(n,'x'),'0x'+format(n,'08x')]
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', key):
                                n=int(key,16); candidates += ['0x'+key,'0x'+format(n,'x'),'0x'+format(n,'08x'),str(n)]
                    except Exception:
                        pass
                    # users.db
                    try:
                        u1 = sqlite3.connect('users.db'); u1.row_factory = sqlite3.Row
                        for k in candidates:
                            ur = u1.execute('SELECT full_name FROM users WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                        if ur and ur['full_name']:
                                uname = ur['full_name']; break
                        u1.close()
                    except Exception:
                        pass
                    # data.db fallback
                    if not uname:
                        try:
                            d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                            for k in candidates:
                                dr = d1.execute('SELECT full_name FROM users WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                            if dr and dr['full_name']:
                                    uname = dr['full_name']; break
                            d1.close()
                        except Exception:
                            pass
                    # device_cards fallback
                    if not uname:
                        try:
                            d2 = sqlite3.connect('data.db'); d2.row_factory = sqlite3.Row
                            for k in candidates:
                                rr = d2.execute('SELECT user_name FROM device_cards WHERE TRIM(LOWER(card_uuid))=TRIM(LOWER(?)) LIMIT 1', (k,)).fetchone()
                            if rr and rr['user_name']:
                                    uname = rr['user_name']; break
                            d2.close()
                        except Exception:
                            pass
                mapped.append({
                    'device_id': (e.get('deviceid') or e.get('deviceId') or device_id),
                    'user_name': uname,
                    'device_name': dev_name or (e.get('deviceid') or e.get('deviceId') or ''),
                    'card_uuid': e.get('uuid') or e.get('UUID') or '',
                    'type_label': (e.get('type') or e.get('Type') or atype_str),
                    'is_granted': True if (isg in (1, True) or isg == 222) else False,
                    'timestamp': e.get('timestamp') or e.get('time') or ''
                })
            # Toplu eşleme (IN sorgusu) ile kalanları çöz
            try:
                unresolved = list({str(i.get('card_uuid')).strip() for i in mapped if (i.get('card_uuid') and not i.get('user_name'))})
                if unresolved:
                    name_map = {}
                    q_marks = ','.join(['?'] * len(unresolved))
                    try:
                        u1 = sqlite3.connect('users.db'); u1.row_factory = sqlite3.Row
                        for ur in u1.execute(f'SELECT user_uuid, card_uuid, full_name FROM users WHERE user_uuid IN ({q_marks}) OR card_uuid IN ({q_marks})', unresolved + unresolved):
                            cu1 = str(ur['user_uuid'] or '').strip()
                            cu2 = str(ur['card_uuid'] or '').strip()
                            fn = ur['full_name']
                            if fn:
                                if cu1:
                                    name_map[cu1] = fn
                                if cu2:
                                    name_map[cu2] = fn
                        u1.close()
                    except Exception:
                        pass
                    try:
                        d1 = sqlite3.connect('data.db'); d1.row_factory = sqlite3.Row
                        for ur in d1.execute(f'SELECT user_uuid, card_uuid, full_name FROM users WHERE user_uuid IN ({q_marks}) OR card_uuid IN ({q_marks})', unresolved + unresolved):
                            cu1 = str(ur['user_uuid'] or '').strip()
                            cu2 = str(ur['card_uuid'] or '').strip()
                            fn = ur['full_name']
                            if fn:
                                if cu1 and cu1 not in name_map:
                                    name_map[cu1] = fn
                                if cu2 and cu2 not in name_map:
                                    name_map[cu2] = fn
                        d1.close()
                    except Exception:
                        pass
                    for it in mapped:
                        if it.get('card_uuid') and not it.get('user_name'):
                            key = str(it['card_uuid']).strip()
                            if key in name_map:
                                it['user_name'] = name_map[key]
            except Exception:
                pass
        except Exception:
            mapped = []
        rows = mapped if mapped else entries
        # PDF oluştur
        pdf = FPDF()
        pdf.add_font('DejaVu', '', 'static/fonts/DejaVuSans.ttf', uni=True)
        pdf.add_page()
        pdf.set_font('DejaVu', '', 14)
        pdf.cell(0, 10, "Device Logs", ln=1, align='C')
        headers = ["Device", "Name", "UUID", "Type", "Status", "Time"]
        pdf.set_font('DejaVu', '', 9)
        pdf.set_fill_color(230, 240, 255)
        col_perc = [20, 25, 20, 10, 10, 15]
        avail_w = pdf.w - pdf.l_margin - pdf.r_margin
        col_widths = [avail_w * p / 100.0 for p in col_perc]
        for i, h in enumerate(headers):
            pdf.cell(col_widths[i], 8, h, border=1, align='C', fill=True)
        pdf.ln()
        def write_row(values):
            pdf.set_font('DejaVu', '', 8)
            for i, (txt, w) in enumerate(zip(values, col_widths)):
                s = str(txt or '')
                max_w = w - 2
                if pdf.get_string_width(s) > max_w:
                    while len(s) > 0 and pdf.get_string_width(s + '...') > max_w:
                        s = s[:-1]
                    s = (s + '...') if s else ''
                pdf.cell(w, 7, s, border=1, align='L')
            pdf.ln()
        # Satırları yaz
        # En son satır ilk satır olacak şekilde sırala (desc)
        try:
            from datetime import datetime as _dt
            def _ts_key(e):
                try:
                    return _dt.strptime(str(e.get('timestamp') or ''), '%d-%m-%y %H:%M:%S')
                except Exception:
                    return _dt.min
            rows = sorted(rows, key=_ts_key, reverse=True)
        except Exception:
            pass
        for e in rows:
            granted = (e.get('is_granted') in (1, True, '1', 'true', 'True', 'YES', 'yes')) if ('is_granted' in e) else (e.get('isgranted') in (1, True, '1', 'true', 'True', 'YES', 'yes'))
            values = [e.get('device_id') or e.get('deviceid') or device_id,
                      (e.get('user_name') if (e.get('user_name') or '').strip() else _('unknown_user')),
                      e.get('card_uuid') or e.get('uuid') or '',
                      (e.get('type_label') or e.get('type') or ''),
                      ('GRANTED' if granted else 'DENIED'),
                      e.get('timestamp') or '']
            write_row(values)

        # Dosya yanıtı
        from flask import make_response
        pdf_out = pdf.output(dest='S')
        if isinstance(pdf_out, (bytes, bytearray)):
            payload = bytes(pdf_out)
        else:
            payload = str(pdf_out).encode('latin1', 'ignore')
        response = make_response(payload)
        response.headers.set('Content-Type', 'application/pdf')
        response.headers.set('Content-Disposition', f'attachment; filename=device_logs_{device_id}_{int(time.time())}.pdf')
        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
        response.headers['Pragma'] = 'no-cache'
        response.headers['Expires'] = '0'
        return response

    @app.route('/device/<device_id>/cards')
    def device_cards_page(device_id):
        if not session.get('logged_in'):
            return redirect(url_for('login'))
        # DB'den kart çekme: KAPALI. Sayfa boş liste ile açılır; kullanıcı "Cihazdan Al" ile doldurur.
        enriched_rows = []
        # Navbar için kullanıcı objesi
        user_info = None
        try:
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                urow = uconn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (session.get('user_id'),)).fetchone()
                uconn.close()
                if urow:
                    user_info = urow
            except Exception:
                user_info = None
            if not user_info:
                dconn = get_db_connection()
                user_info = dconn.execute('SELECT id, email, full_name, company, profile_image FROM users WHERE id = ?', (session.get('user_id'),)).fetchone()
                dconn.close()
        except Exception:
            user_info = None
        return render_template(
            'device_cards.html',
            device_id=device_id,
            cards=enriched_rows,
            user=user_info,
            user_name=session.get('user_name') or '',
            current_language=session.get('dil', 'tr'),
            static_bust=int(time.time())
        )

    # Cihaz kartlarını eşitle (getcards)
    @app.route('/api/device/<device_id>/cards/sync', methods=['POST'])
    def api_device_cards_sync(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': TRANSLATIONS.get(get_locale(), TRANSLATIONS['tr'])['device_not_connected']}), 400
            ok = tcp_manager.send_command_to_device(str(device_id), 'getcards')
            if not ok:
                return jsonify({'success': False, 'message': 'Komut gönderilemedi'}), 500
            # readfinished bekle (kartlar toplanıp response set edilecek)
            resp = tcp_manager.get_command_response(str(device_id), timeout=30)
            # Cihazdan gelen yanıttaki kart listesini kullan (DB'den alma) ve kullanıcı eşlemesi yap
            raw_cards = []
            if isinstance(resp, dict) and isinstance(resp.get('cards'), list):
                raw_cards = resp.get('cards')
            # Kullanıcı adı eşlemesi: atanmış + global (users.db ve data.db) kaynaklarından normalleştirilmiş (lowercase, hex/dec) harita kur
            user_map = {}
            def add_uuid_forms_to_map(card_uuid_value, full_name_value):
                    s = str(card_uuid_value or '').strip()
                    if not s:
                        return
                    s_lower = s.lower()
                    if s_lower == 'mastercard':
                        return
                    if s_lower not in user_map:
                        user_map[s_lower] = full_name_value or ''
                    try:
                        if s_lower.startswith('0x'):
                            n = int(s_lower, 16)
                            dec_str = str(n)
                            hex_norm = '0x' + format(n, 'x')
                            hex_norm8 = '0x' + format(n, '08x')
                            for k in [dec_str, hex_norm, hex_norm8]:
                                if k not in user_map:
                                    user_map[k] = full_name_value or ''
                        elif s_lower.isdigit():
                            n = int(s_lower, 10)
                            dec_str = str(n)
                            hex_norm = '0x' + format(n, 'x')
                            hex_norm8 = '0x' + format(n, '08x')
                            for k in [dec_str, hex_norm, hex_norm8]:
                                if k not in user_map:
                                    user_map[k] = full_name_value or ''
                        else:
                            import re as _re
                            if _re.fullmatch(r'[0-9a-f]+', s_lower):
                                n = int(s_lower, 16)
                                dec_str = str(n)
                                hex_norm = '0x' + format(n, 'x')
                                hex_norm8 = '0x' + format(n, '08x')
                                preserved_hex = '0x' + s_lower
                                for k in [preserved_hex, dec_str, hex_norm, hex_norm8]:
                                    if k not in user_map:
                                        user_map[k] = full_name_value or ''
                    except Exception:
                        pass
            # 1) Atanmış kullanıcılar (users.db)
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                assigned = uconn.execute('SELECT u.full_name, u.card_uuid FROM users u JOIN user_device_access m ON m.user_id = u.id WHERE m.device_id = ? AND u.card_uuid IS NOT NULL AND TRIM(u.card_uuid)<>""', (str(device_id),)).fetchall()
                for r in assigned:
                    add_uuid_forms_to_map(r['card_uuid'], r['full_name'])
                uconn.close()
            except Exception:
                pass
            # 2) Atanmış kullanıcılar (data.db)
            try:
                dconn = get_db_connection()
                assigned2 = dconn.execute('SELECT u.full_name, u.card_uuid FROM users u JOIN user_device_access m ON m.user_id = u.id WHERE m.device_id = ? AND u.card_uuid IS NOT NULL AND TRIM(u.card_uuid)<>""', (str(device_id),)).fetchall()
                for r in assigned2:
                    add_uuid_forms_to_map(r['card_uuid'], r['full_name'])
                dconn.close()
            except Exception:
                pass
            # Global fallback kapalı: sadece cihaza yetkili kullanıcılar kullanılacak
            pass
        except Exception:
            pass
        # Kartları zenginleştir ve MASTERCARD'ı hariç tut
        enriched = []
        for c in raw_cards:
            try:
                if isinstance(c, dict):
                    cu = str((c.get('card_uuid') or c.get('uuid') or c.get('id') or '')).strip()
                    if cu.upper() == 'MASTERCARD':
                        continue
                    tmode = bool(c.get('terminal_mode_access')) if 'terminal_mode_access' in c else False
                    created = c.get('created_at') or '-'
                    key = cu.strip().lower()
                    uname = ''
                    candidates = [key]
                    try:
                        if key.startswith('0x'):
                            n = int(key, 16)
                            candidates += [str(n), '0x' + format(n, 'x'), '0x' + format(n, '08x')]
                        elif key.isdigit():
                            n = int(key, 10)
                            candidates += [str(n), '0x' + format(n, 'x'), '0x' + format(n, '08x')]
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', key):
                                n = int(key, 16)
                                candidates += ['0x' + key, '0x' + format(n, 'x'), '0x' + format(n, '08x'), str(n)]
                    except Exception:
                        pass
                    for k in candidates:
                        if k in user_map:
                            uname = user_map[k]
                            break
                    enriched.append({
                        'card_uuid': cu,
                        'user_name': uname or '',
                        'terminal_mode_access': tmode,
                        'created_at': created
                    })
                else:
                    cu = str(c).strip()
                    if cu.upper() == 'MASTERCARD':
                        continue
                    key = cu.strip().lower()
                    uname = ''
                    candidates = [key]
                    try:
                        if key.startswith('0x'):
                            n = int(key, 16)
                            candidates += [str(n), '0x' + format(n, 'x'), '0x' + format(n, '08x')]
                        elif key.isdigit():
                            n = int(key, 10)
                            candidates += [str(n), '0x' + format(n, 'x'), '0x' + format(n, '08x')]
                        else:
                            import re
                            if re.fullmatch(r'[0-9a-f]+', key):
                                n = int(key, 16)
                                candidates += ['0x' + key, '0x' + format(n, 'x'), '0x' + format(n, '08x'), str(n)]
                    except Exception:
                        pass
                    for k in candidates:
                        if k in user_map:
                            uname = user_map[k]
                            break
                    enriched.append({
                        'card_uuid': cu,
                        'user_name': uname or '',
                        'terminal_mode_access': False,
                        'created_at': '-'
                    })
            except Exception:
                continue
        return jsonify({'success': True, 'cards': enriched})

    # Cihaz kartlarını sil (DB temizle + cihaza formatcards sinyali gönderme denemesi)

    # 50 random NFC kullanıcı oluştur (admin)
    @app.route('/api/admin/generate_random_nfc_users', methods=['POST'])
    def api_generate_random_nfc_users():
        if not session.get('is_admin'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            created = user_manager.generate_random_nfc_users(50)
            return jsonify({'success': True, 'created': created})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    # Cihaza NFC kartlarını partiler halinde gönder (max 50/adım)
    @app.route('/api/device/<device_id>/cards/push', methods=['POST'])
    def api_device_cards_push(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': TRANSLATIONS.get(get_locale(), TRANSLATIONS['tr'])['device_not_connected']}), 400
            # Bu cihaza atanmış kullanıcıların NFC kart UUID'lerini topla (users.db ve data.db)
            assigned_user_ids = set()
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                for r in uconn.execute('SELECT user_id FROM user_device_access WHERE device_id = ?', (str(device_id),)):
                    assigned_user_ids.add(int(r['user_id']))
                uconn.close()
            except Exception:
                pass
            try:
                dconn = get_db_connection()
                for r in dconn.execute('SELECT user_id FROM user_device_access WHERE device_id = ?', (str(device_id),)):
                    try:
                        assigned_user_ids.add(int(r['user_id']))
                    except Exception:
                        pass
                dconn.close()
            except Exception:
                pass
            if not assigned_user_ids:
                return jsonify({'success': True, 'sent': 0, 'message': 'No assigned users'}), 200
            # Kart UUID'leri
            card_uuids = []
            try:
                uconn2 = sqlite3.connect('users.db'); uconn2.row_factory = sqlite3.Row
                qmarks = ','.join(['?'] * len(assigned_user_ids))
                rows = uconn2.execute(f"SELECT card_uuid FROM users WHERE id IN ({qmarks}) AND card_uuid IS NOT NULL AND TRIM(card_uuid)<>'' AND (full_name IS NULL OR UPPER(full_name) <> 'MASTERCARD')", list(assigned_user_ids)).fetchall()
                card_uuids.extend([str(r['card_uuid']).strip() for r in rows if r['card_uuid']])
                uconn2.close()
            except Exception:
                pass
            try:
                dconn2 = get_db_connection()
                qmarks = ','.join(['?'] * len(assigned_user_ids))
                rows = dconn2.execute(f"SELECT card_uuid FROM users WHERE id IN ({qmarks}) AND card_uuid IS NOT NULL AND TRIM(card_uuid)<>'' AND (full_name IS NULL OR UPPER(full_name) <> 'MASTERCARD')", list(assigned_user_ids)).fetchall()
                card_uuids.extend([str(r['card_uuid']).strip() for r in rows if r['card_uuid']])
                dconn2.close()
            except Exception:
                pass
            # Hex'e dönüştür
            def to_hex(u):
                s = str(u or '').strip()
                if not s:
                    return None
                if s.lower().startswith('0x'):
                    return s
                try:
                    n = int(s, 10)
                    return '0x' + format(n, 'x')
                except Exception:
                    return None
            hex_cards = [h for h in [to_hex(u) for u in card_uuids] if h]
            # 50'lik partilere böl ve gönder
            def chunks(lst, n):
                for i in range(0, len(lst), n):
                    yield lst[i:i+n]
            # Duplicateleri kaldır (case-insensitive) ve sıralamayı koru
            uniq = []
            seen = set()
            for h in hex_cards:
                key = (h or '').lower()
                if key and key not in seen:
                    uniq.append(h)
                    seen.add(key)
            paused_prev = getattr(tcp_manager, 'pause_periodic_getdeviceinfo', False)
            tcp_manager.pause_periodic_getdeviceinfo = True
            try:
                sent = 0
                for idx, batch in enumerate(chunks(uniq, 50), start=1):
                    payload = json.dumps({'addcards': batch})
                    ok = tcp_manager.send_command_to_device(str(device_id), payload)
                    if not ok:
                        return jsonify({'success': False, 'message': 'Komut gönderilemedi', 'sent': sent}), 500
                    resp = tcp_manager.get_command_response(str(device_id), timeout=30)
                    sent += len(batch)
                    if not resp:
                        continue
                    msg = json.dumps(resp)
                    low = msg.lower()
                    if 'setfail' in low or 'savefailed' in low or 'jsonfailed' in low:
                        return jsonify({'success': False, 'message': 'savefailed', 'sent': sent}), 200
                    if 'nomorespace' in low:
                        try:
                            tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                            tcp_manager.get_command_response(str(device_id), timeout=30)
                        except Exception:
                            pass
                        return jsonify({'success': True, 'message': 'nomorespace', 'sent': sent}), 200
                # Tüm batch'ler tamamlandı: sonunda updatecards gönder
                try:
                    tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                    tcp_manager.get_command_response(str(device_id), timeout=30)
                except Exception:
                    pass
                return jsonify({'success': True, 'sent': sent})
            finally:
                tcp_manager.pause_periodic_getdeviceinfo = paused_prev
        except Exception as e:
            logger.error(f"/api/device/{device_id}/cards/push error: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/device/<device_id>/cards/push_candidates')
    def api_device_cards_push_candidates(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            assigned_user_ids = set()
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                for r in uconn.execute('SELECT user_id FROM user_device_access WHERE device_id = ?', (str(device_id),)):
                    assigned_user_ids.add(int(r['user_id']))
                uconn.close()
            except Exception:
                pass
            try:
                dconn = get_db_connection()
                for r in dconn.execute('SELECT user_id FROM user_device_access WHERE device_id = ?', (str(device_id),)):
                    try:
                        assigned_user_ids.add(int(r['user_id']))
                    except Exception:
                        pass
                dconn.close()
            except Exception:
                pass
            card_uuids = []
            if assigned_user_ids:
                try:
                    uconn2 = sqlite3.connect('users.db'); uconn2.row_factory = sqlite3.Row
                    qmarks = ','.join(['?'] * len(assigned_user_ids))
                    rows = uconn2.execute(f"SELECT card_uuid FROM users WHERE id IN ({qmarks}) AND card_uuid IS NOT NULL AND TRIM(card_uuid)<>'' AND (full_name IS NULL OR UPPER(full_name) <> 'MASTERCARD')", list(assigned_user_ids)).fetchall()
                    card_uuids.extend([str(r['card_uuid']).strip() for r in rows if r['card_uuid']])
                    uconn2.close()
                except Exception:
                    pass
                try:
                    dconn2 = get_db_connection()
                    qmarks = ','.join(['?'] * len(assigned_user_ids))
                    rows = dconn2.execute(f"SELECT card_uuid FROM users WHERE id IN ({qmarks}) AND card_uuid IS NOT NULL AND TRIM(card_uuid)<>'' AND (full_name IS NULL OR UPPER(full_name) <> 'MASTERCARD')", list(assigned_user_ids)).fetchall()
                    card_uuids.extend([str(r['card_uuid']).strip() for r in rows if r['card_uuid']])
                    dconn2.close()
                except Exception:
                    pass
            def to_hex(u):
                s = str(u or '').strip()
                if not s:
                    return None
                if s.lower().startswith('0x'):
                    return s
                try:
                    n = int(s, 10)
                    return '0x' + format(n, 'x')
                except Exception:
                    return None
            hex_cards = [h for h in [to_hex(u) for u in card_uuids] if h]
            seen = set(); uniq = []
            for h in hex_cards:
                key = h.lower()
                if key not in seen:
                    uniq.append(h)
                    seen.add(key)
            return jsonify({'success': True, 'cards': uniq, 'total': len(uniq)})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/device/<device_id>/cards/push_batch', methods=['POST'])
    def api_device_cards_push_batch(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': TRANSLATIONS.get(get_locale(), TRANSLATIONS['tr'])['device_not_connected']}), 400
            data = request.get_json(silent=True) or {}
            cards = data.get('cards') or []
            if not isinstance(cards, list) or not cards:
                return jsonify({'success': False, 'message': 'No cards provided'}), 400
            if len(cards) > 50:
                return jsonify({'success': False, 'message': 'Max 50 cards per batch'}), 400
            paused_prev = getattr(tcp_manager, 'pause_periodic_getdeviceinfo', False)
            tcp_manager.pause_periodic_getdeviceinfo = True
            try:
                payload = json.dumps({'addcards': cards})
                ok = tcp_manager.send_command_to_device(str(device_id), payload)
                if not ok:
                    return jsonify({'success': False, 'message': 'Komut gönderilemedi'}), 500
                resp = tcp_manager.get_command_response(str(device_id), timeout=30)
                msg = json.dumps(resp) if resp else ''
                low = msg.lower()
                if 'nomorespace' in low:
                    try:
                        tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                        tcp_manager.get_command_response(str(device_id), timeout=30)
                    except Exception:
                        pass
                    return jsonify({'success': True, 'message': 'nomorespace', 'sent': len(cards)})
                if ('savefailed' in low) or ('setfail' in low) or ('jsonfailed' in low):
                    try:
                        tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                        tcp_manager.get_command_response(str(device_id), timeout=30)
                    except Exception:
                        pass
                    return jsonify({'success': False, 'message': 'savefailed', 'sent': 0}), 200
                # updatecards after any outcome (including saveok/setok)
                try:
                    tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                    resp2 = tcp_manager.get_command_response(str(device_id), timeout=30)
                    low2 = (json.dumps(resp2).lower() if resp2 else '')
                    if ('savefailed' in low2) or ('setfail' in low2) or ('jsonfailed' in low2):
                        return jsonify({'success': False, 'message': 'updatecards_savefailed', 'sent': len(cards)})
                except Exception:
                    pass
                return jsonify({'success': True, 'sent': len(cards)})
            finally:
                tcp_manager.pause_periodic_getdeviceinfo = paused_prev
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/device/<device_id>/cards/clear', methods=['POST'])
    def api_device_cards_clear(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            # Sadece cihaza formatcards gönder (cihaz içindeki kartlar silinir, DB'dekilere dokunma)
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': TRANSLATIONS.get(get_locale(), TRANSLATIONS['tr'])['device_not_connected']}), 400
            threading.Thread(target=lambda: tcp_manager.send_command_to_device(str(device_id), 'formatcards'), daemon=True).start()
            return jsonify({'success': True})
        except Exception as e:
            logger.error(f"/api/device/{device_id}/cards/clear error: {e}")
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/device/<device_id>/cards/delete', methods=['POST'])
    def api_device_cards_delete(device_id):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            data = request.get_json(silent=True) or {}
            uuids = data.get('uuids') or data.get('cards') or []
            if not isinstance(uuids, list) or not uuids:
                return jsonify({'success': False, 'message': 'No cards provided'}), 400
            # Max 50 per request constraint
            if len(uuids) > 50:
                return jsonify({'success': False, 'message': 'You can delete at most 50 cards per request'}), 400
            if not tcp_manager.is_device_connected(str(device_id)):
                return jsonify({'success': False, 'message': TRANSLATIONS.get(get_locale(), TRANSLATIONS['tr'])['device_not_connected']}), 400
            # Normalize to hex strings acceptable by device
            def to_hex(u):
                s = str(u or '').strip()
                if not s:
                    return None
                if s.lower().startswith('0x'):
                    return s
                try:
                    n = int(s, 10)
                    return '0x' + format(n, 'x')
                except Exception:
                    return None
            hex_cards = [h for h in (to_hex(u) for u in uuids) if h]
            if not hex_cards:
                return jsonify({'success': False, 'message': 'No valid card UUIDs'}), 400
            # Send deletecards
            paused_prev = getattr(tcp_manager, 'pause_periodic_getdeviceinfo', False)
            tcp_manager.pause_periodic_getdeviceinfo = True
            try:
                payload = json.dumps({'deletecards': hex_cards})
                ok = tcp_manager.send_command_to_device(str(device_id), payload)
                if not ok:
                    return jsonify({'success': False, 'message': 'Komut gönderilemedi'}), 500
                resp = tcp_manager.get_command_response(str(device_id), timeout=30)
                if resp and isinstance(resp, dict):
                    msg = json.dumps(resp).lower()
                    if 'savefailed' in msg or 'setfail' in msg or 'nomorespace' in msg:
                        return jsonify({'success': False, 'message': 'Device reported failure'}), 500
                    # saveok veya setok durumunda updatecards raw komutu gönder
                    if 'saveok' in msg or 'setok' in msg:
                        try:
                            tcp_manager.send_command_to_device(str(device_id), 'updatecards')
                        except Exception:
                            pass
                    # Return refreshed list by refetching from device (no DB)
                    try:
                        ok2 = tcp_manager.send_command_to_device(str(device_id), 'getcards')
                        if ok2:
                            resp2 = tcp_manager.get_command_response(str(device_id), timeout=30)
                        else:
                            resp2 = None
                    except Exception:
                        resp2 = None
            except Exception:
                resp2 = None
            finally:
                tcp_manager.pause_periodic_getdeviceinfo = paused_prev
            raw_cards = []
            if isinstance(resp2, dict) and isinstance(resp2.get('cards'), list):
                raw_cards = resp2.get('cards')
            # Kullanıcı adı eşlemesi: atanmış + global (users.db ve data.db) kaynaklarından normalleştirilmiş (lowercase, hex/dec) harita kur
            user_map = {}
            def add_uuid_forms_to_map(card_uuid_value, full_name_value):
                        s = str(card_uuid_value or '').strip()
                        if not s:
                            return
                        s_lower = s.lower()
                        if s_lower == 'mastercard':
                            return
                        if s_lower not in user_map:
                            user_map[s_lower] = full_name_value or ''
                        try:
                            if s_lower.startswith('0x'):
                                n = int(s_lower, 16)
                                dec_str = str(n)
                                hex_norm = '0x' + format(n, 'x')
                                hex_norm8 = '0x' + format(n, '08x')
                                for k in [dec_str, hex_norm, hex_norm8]:
                                    if k not in user_map:
                                        user_map[k] = full_name_value or ''
                            elif s_lower.isdigit():
                                n = int(s_lower, 10)
                                dec_str = str(n)
                                hex_norm = '0x' + format(n, 'x')
                                hex_norm8 = '0x' + format(n, '08x')
                                for k in [dec_str, hex_norm, hex_norm8]:
                                    if k not in user_map:
                                        user_map[k] = full_name_value or ''
                            else:
                                import re as _re
                                if _re.fullmatch(r'[0-9a-f]+', s_lower):
                                    n = int(s_lower, 16)
                                    dec_str = str(n)
                                    hex_norm = '0x' + format(n, 'x')
                                    hex_norm8 = '0x' + format(n, '08x')
                                    preserved_hex = '0x' + s_lower
                                    for k in [preserved_hex, dec_str, hex_norm, hex_norm8]:
                                        if k not in user_map:
                                            user_map[k] = full_name_value or ''
            # 1) Atanmış kullanıcılar (users.db)
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                assigned = uconn.execute('SELECT u.full_name, u.card_uuid FROM users u JOIN user_device_access m ON m.user_id = u.id WHERE m.device_id = ? AND u.card_uuid IS NOT NULL AND TRIM(u.card_uuid)<>""', (str(device_id),)).fetchall()
                for r in assigned:
                    add_uuid_forms_to_map(r['card_uuid'], r['full_name'])
                uconn.close()
            except Exception:
                pass
            # 2) Atanmış kullanıcılar (data.db)
            try:
                dconn = get_db_connection()
                assigned2 = dconn.execute('SELECT u.full_name, u.card_uuid FROM users u JOIN user_device_access m ON m.user_id = u.id WHERE m.device_id = ? AND u.card_uuid IS NOT NULL AND TRIM(u.card_uuid)<>""', (str(device_id),)).fetchall()
                for r in assigned2:
                    add_uuid_forms_to_map(r['card_uuid'], r['full_name'])
                dconn.close()
            except Exception:
                pass
            # Global fallback kapalı: sadece cihaza yetkili kullanıcılar kullanılacak
            pass
        except Exception:
            pass
        cards = []
        for c in raw_cards:
            try:
                if isinstance(c, dict):
                    cu = str((c.get('card_uuid') or c.get('uuid') or c.get('id') or '')).strip()
                    if cu.upper() == 'MASTERCARD':
                        continue
                    key = cu.strip().lower()
                    uname = user_map.get(key, '')
                    if not uname:
                        try:
                            if key.startswith('0x'):
                                decv = str(int(key, 16))
                                uname = user_map.get(decv, '')
                            else:
                                hxv = '0x' + format(int(key, 10), 'x')
                                uname = user_map.get(hxv, '')
                        except Exception:
                            pass
                    cards.append({'card_uuid': cu, 'user_name': uname or ''})
                else:
                    cu = str(c).strip()
                    if cu.upper() == 'MASTERCARD':
                        continue
                    key = cu.strip().lower()
                    uname = user_map.get(key, '')
                    if not uname:
                        try:
                            hxv = '0x' + format(int(key, 10), 'x')
                            uname = user_map.get(hxv, '')
                        except Exception:
                            pass
                    cards.append({'card_uuid': cu, 'user_name': uname or ''})
            except Exception:
                continue
        return jsonify({'success': True, 'deleted': len(hex_cards), 'cards': cards})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500

    @app.route('/api/access_logs/latest')
    def api_access_logs_latest():
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            # devices as repeated query param: /api/access_logs/latest?device=SN1&device=SN2&type=nfc&afterId=123
            device_ids = request.args.getlist('device')
            after_id = request.args.get('afterId', type=int, default=0)
            type_param = (request.args.get('type') or '').strip().lower()
            conn = get_db_connection(); conn.row_factory = sqlite3.Row
            base_sql = 'SELECT id, device_id, access_type, card_uuid, is_terminal, is_granted, timestamp FROM access_logs'
            params = []
            clauses = []
            if device_ids:
                marks = ','.join(['?'] * len(device_ids))
                clauses.append(f'device_id IN ({marks})')
                params.extend(device_ids)
            if type_param in ('nfc', '1'):
                clauses.append('access_type = 1')
            if after_id and after_id > 0:
                clauses.append('id > ?')
                params.append(after_id)
            if clauses:
                base_sql += ' WHERE ' + ' AND '.join(clauses)
            base_sql += ' ORDER BY id DESC LIMIT 50'
            rows = conn.execute(base_sql, params).fetchall()
            conn.close()
            logs = [
                {
                    'id': r['id'],
                    'device_id': r['device_id'],
                    'access_type': r['access_type'],
                    'card_uuid': r['card_uuid'],
                    'is_terminal': r['is_terminal'],
                    'is_granted': r['is_granted'],
                    'timestamp': r['timestamp']
                } for r in rows
            ]
            return jsonify({'success': True, 'logs': logs})
        except Exception as e:
            logger.error(f"/api/access_logs/latest error: {e}")
            return jsonify({'success': False, 'message': str(e), 'logs': []}), 500



 
    @app.route('/api/user/<int:user_id>')
    def api_user_details(user_id: int):
        if not session.get('logged_in'):
            return jsonify({'success': False, 'message': 'Unauthorized'}), 401
        try:
            user_row = None
            try:
                uconn = sqlite3.connect('users.db'); uconn.row_factory = sqlite3.Row
                user_row = uconn.execute('SELECT id, full_name, email, company, user_type, access_type, is_active, card_uuid, phone, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                uconn.close()
            except Exception:
                user_row = None
            if not user_row:
                try:
                    dconn = get_db_connection()
                    user_row = dconn.execute('SELECT id, full_name, email, company, user_type, access_type, is_active, card_uuid, phone, profile_image FROM users WHERE id = ?', (user_id,)).fetchone()
                    dconn.close()
                except Exception:
                    user_row = None
            if not user_row:
                return jsonify({'success': False, 'message': 'User not found'}), 404
            device_ids = set(); devices = []
            try:
                uconn2 = sqlite3.connect('users.db'); uconn2.row_factory = sqlite3.Row
                for r in uconn2.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)):
                    device_ids.add(str(r['device_id']))
                uconn2.close()
            except Exception:
                pass
            try:
                dconn2 = get_db_connection()
                for r in dconn2.execute('SELECT device_id FROM user_device_access WHERE user_id = ?', (user_id,)):
                    device_ids.add(str(r['device_id']))
                id_to_name = {}
                try:
                    rows = dconn2.execute('SELECT id, ad FROM cihazlar').fetchall()
                    id_to_name = {str(r['id']): (r['ad'] or '') for r in rows}
                except Exception:
                    id_to_name = {}
                dconn2.close()
                for did in device_ids:
                    devices.append({'id': did, 'name': id_to_name.get(str(did), str(did))})
            except Exception:
                devices = [{'id': did, 'name': str(did)} for did in device_ids]
            user = {
                'id': user_row['id'],
                'full_name': user_row['full_name'],
                'email': user_row['email'],
                'company': user_row['company'],
                'user_type': user_row['user_type'],
                'access_type': user_row['access_type'],
                'is_active': bool(user_row['is_active']) if user_row['is_active'] is not None else False,
                'card_uuid': user_row['card_uuid'],
                'phone': user_row['phone'],
                'profile_image': user_row['profile_image'],
                'devices': devices,
            }
            return jsonify({'success': True, 'user': user})
        except Exception as e:
            return jsonify({'success': False, 'message': str(e)}), 500
 