const { requireAdminAuth } = require('../../src/middlewares/auth');
const AdminAuthService = require('../../src/services/AdminAuthService');
const AdminUserRepository = require('../../src/repositories/AdminUserRepository');
const dbManager = require('../../src/database/DatabaseManager');

describe('Auth Middleware Unit Test (Session based)', () => {
  let req, res, next;

  beforeEach(() => {
    req = { session: null };
    res = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn(),
      locals: {}
    };
    next = jest.fn();
  });

  test('should reject when no session exists', () => {
    requireAdminAuth(req, res, next);

    expect(res.status).toHaveBeenCalledWith(403);
    expect(res.json).toHaveBeenCalledWith(
      expect.objectContaining({
        error: 'Zugriff verweigert',
        reason: 'SESSION_REQUIRED'
      })
    );
    expect(next).not.toHaveBeenCalled();
  });

  test('should reject when session user is missing', () => {
    req.session = {};

    requireAdminAuth(req, res, next);

    expect(res.status).toHaveBeenCalledWith(403);
    expect(res.json).toHaveBeenCalledWith(
      expect.objectContaining({ reason: 'SESSION_REQUIRED' })
    );
    expect(next).not.toHaveBeenCalled();
  });

  test('should reject non-admin roles', () => {
    req.session = { user: { id: 1, role: 'viewer' } };

    requireAdminAuth(req, res, next);

    expect(res.status).toHaveBeenCalledWith(403);
    expect(res.json).toHaveBeenCalledWith(
      expect.objectContaining({ reason: 'SESSION_REQUIRED' })
    );
    expect(next).not.toHaveBeenCalled();
  });

  test('should pass through for admin sessions and expose user on locals', () => {
    const adminUser = { id: 1, role: 'admin', username: 'testadmin' };
    req.session = { user: adminUser };

    requireAdminAuth(req, res, next);

    expect(next).toHaveBeenCalled();
    expect(res.status).not.toHaveBeenCalled();
    expect(res.locals.adminUser).toEqual(adminUser);
  });
});

describe('AdminAuthService', () => {
  beforeEach(async () => {
    await dbManager.run('DELETE FROM admin_users');
  });

  afterEach(async () => {
    await dbManager.run('DELETE FROM admin_users');
  });

  test('needsInitialSetup reflects admin count', async () => {
    await expect(AdminAuthService.needsInitialSetup()).resolves.toBe(true);

    await AdminAuthService.createInitialAdmin({
      username: 'existing',
      password: 'SuperSecure123!'
    });

    await expect(AdminAuthService.needsInitialSetup()).resolves.toBe(false);
  });

  test('createInitialAdmin validates input and detects completed setup', async () => {
    await expect(
      AdminAuthService.createInitialAdmin({ username: '', password: 'SuperSecure123!' })
    ).rejects.toThrow('USERNAME_REQUIRED');

    await expect(
      AdminAuthService.createInitialAdmin({ username: 'admin', password: 'short' })
    ).rejects.toThrow('PASSWORD_TOO_WEAK');

    await AdminAuthService.createInitialAdmin({ username: 'seed', password: 'SuperSecure123!' });
    await expect(
      AdminAuthService.createInitialAdmin({ username: 'admin', password: 'SuperSecure123!' })
    ).rejects.toThrow('SETUP_ALREADY_COMPLETED');
  });

  test('createInitialAdmin persists normalized admin when setup allowed', async () => {
    const result = await AdminAuthService.createInitialAdmin({
      username: 'TestAdmin',
      password: 'SuperSecure123!'
    });

    expect(result.username).toBe('testadmin');
    expect(result.role).toBe('admin');

    const stored = await AdminUserRepository.getByUsername('testadmin');
    expect(stored).toMatchObject({ username: 'testadmin', role: 'admin', is_active: 1 });
  });

  test('verifyCredentials handles missing users and password mismatches', async () => {
    await expect(AdminAuthService.verifyCredentials('admin', 'pw')).resolves.toBeNull();

    const hash = await AdminAuthService.hashPassword('SuperSecure123!');
    await AdminUserRepository.createAdminUser({
      username: 'admin',
      passwordHash: hash,
      role: 'admin',
      requiresPasswordChange: false
    });

    await expect(AdminAuthService.verifyCredentials('admin', 'wrong')).resolves.toBeNull();
  });

  test('verifyCredentials returns sanitized user for valid credentials', async () => {
    const hash = await AdminAuthService.hashPassword('SuperSecure123!');
    await AdminUserRepository.createAdminUser({
      username: 'admin',
      passwordHash: hash,
      role: 'admin',
      requiresPasswordChange: true
    });

    const result = await AdminAuthService.verifyCredentials('admin', 'SuperSecure123!');

    expect(result).toEqual({
      id: expect.any(Number),
      username: 'admin',
      role: 'admin',
      requiresPasswordChange: true
    });
  });
});
