import { randomUUID } from 'crypto';
import { Router } from 'express';
import db from '../../db.js';
import { getSyncStatus, performLdapSync } from '../../ldap-sync.js';
import { badRequest, UUID } from '../../lib/http.js';
import { mapPublicUser } from '../../lib/mappers.js';
import {
  loadIntegrations,
  restartLdapSyncScheduler,
  saveIntegrations,
} from '../../integrations.js';
import { hashPassword } from '../../password.js';
import { requireAdmin, requireAuth } from '../../middleware/auth.js';

export function createAdminRouter() {
  const admin = Router();
  admin.use(requireAuth, requireAdmin);

  admin.get('/users', (_req, res) => {
    const rows = db
      .prepare(
        'SELECT id, username, role, source, active, ldap_dn, created_at, updated_at FROM users ORDER BY username ASC',
      )
      .all();
    res.json(rows.map(mapPublicUser));
  });

  admin.post('/users', async (req, res) => {
    const { username, password, role } = req.body || {};
    const un = String(username || '')
      .trim()
      .toLowerCase();
    if (!un || !password) return badRequest(res, 'Benutzername und Passwort erforderlich.');
    const r = role === 'admin' ? 'admin' : 'user';
    const id = randomUUID();
    const ph = await hashPassword(password);
    try {
      db.prepare(
        `INSERT INTO users (id, username, password_hash, role, source, active, updated_at)
       VALUES (?, ?, ?, ?, 'local', 1, datetime('now'))`,
      ).run(id, un, ph, r);
    } catch (e) {
      if (String(e.message || e).includes('UNIQUE')) {
        return res.status(409).json({ message: 'Benutzername bereits vergeben.' });
      }
      throw e;
    }
    const row = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    res.status(201).json(mapPublicUser(row));
  });

  admin.put('/users/:id', async (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const cur = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    if (!cur) return res.status(404).json({ message: 'Nicht gefunden' });
    const b = req.body || {};

    if (b.password != null && String(b.password).length > 0) {
      if (cur.source !== 'local') {
        return badRequest(res, 'Passwort nur für lokale Benutzer änderbar.');
      }
      const ph = await hashPassword(b.password);
      db.prepare(
        'UPDATE users SET password_hash = ?, updated_at = datetime(\'now\') WHERE id = ?',
      ).run(ph, id);
    }
    if (b.role !== undefined) {
      if (b.role !== 'admin' && b.role !== 'user') {
        return badRequest(res, 'Ungültige Rolle.');
      }
      const admins = db
        .prepare(
          "SELECT COUNT(*) AS c FROM users WHERE role = 'admin' AND active = 1",
        )
        .get().c;
      if (cur.role === 'admin' && b.role === 'user' && admins <= 1) {
        return res.status(400).json({ message: 'Letzter Administrator kann nicht herabgestuft werden.' });
      }
      db.prepare('UPDATE users SET role = ?, updated_at = datetime(\'now\') WHERE id = ?').run(
        b.role,
        id,
      );
    }
    if (b.active !== undefined) {
      const active = b.active ? 1 : 0;
      const admins = db
        .prepare(
          "SELECT COUNT(*) AS c FROM users WHERE role = 'admin' AND active = 1",
        )
        .get().c;
      if (cur.role === 'admin' && cur.active && !active && admins <= 1) {
        return res.status(400).json({ message: 'Letzter Administrator kann nicht deaktiviert werden.' });
      }
      db.prepare('UPDATE users SET active = ?, updated_at = datetime(\'now\') WHERE id = ?').run(
        active,
        id,
      );
    }

    const row = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    res.json(mapPublicUser(row));
  });

  admin.delete('/users/:id', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    if (id === req.session.userId) {
      return res.status(400).json({ message: 'Eigenes Konto kann nicht gelöscht werden.' });
    }
    const cur = db.prepare('SELECT * FROM users WHERE id = ?').get(id);
    if (!cur) return res.status(404).json({ message: 'Nicht gefunden' });
    if (cur.role === 'admin') {
      const admins = db
        .prepare(
          "SELECT COUNT(*) AS c FROM users WHERE role = 'admin' AND active = 1",
        )
        .get().c;
      if (admins <= 1) {
        return res.status(400).json({ message: 'Letzter Administrator kann nicht gelöscht werden.' });
      }
    }
    db.prepare('DELETE FROM users WHERE id = ?').run(id);
    res.json({ ok: true });
  });

  admin.get('/settings/integrations', (_req, res) => {
    res.json(loadIntegrations());
  });

  admin.put('/settings/integrations', (req, res) => {
    const b = req.body || {};
    const cur = loadIntegrations();
    if (b.ldap && typeof b.ldap === 'object') {
      const incoming = { ...b.ldap };
      if (incoming.bindPassword === '' || incoming.bindPassword == null) {
        delete incoming.bindPassword;
      }
      if (incoming.syncIntervalMinutes != null) {
        const n = Number(incoming.syncIntervalMinutes);
        incoming.syncIntervalMinutes = Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 1440;
      }
      Object.assign(cur.ldap, incoming);
      if (b.ldap.userSearchFilter != null) {
        cur.ldap.userFilter = String(b.ldap.userSearchFilter);
      }
    }
    if (b.teamviewer && typeof b.teamviewer === 'object') {
      const inc = { ...b.teamviewer };
      if (inc.bearerToken === '' || inc.bearerToken == null) {
        delete inc.bearerToken;
      }
      Object.assign(cur.teamviewer, inc);
    }
    saveIntegrations(cur);
    restartLdapSyncScheduler();
    res.json(loadIntegrations());
  });

  admin.get('/ldap/sync-status', (_req, res) => {
    res.json(getSyncStatus(db));
  });

  admin.post('/ldap/sync', async (_req, res) => {
    const r = await performLdapSync(db, loadIntegrations, 'manual');
    if (r.skipped) {
      return res.status(409).json({ message: r.message });
    }
    res.json(r);
  });

  return admin;
}
