import { Router } from 'express';
import { v4 as uuidv4 } from 'uuid';
import {
  getUserById,
  getUserByUsername,
  registerUser,
  updateUserPasswordHash,
  updateUserSigningPublicKey
} from '../cqrs';
import { hashPasswordForStorage, verifyPassword } from '../services/password-auth.service';
import { issueSessionToken, revokeSessionToken } from '../services/session-auth.service';
import { getAuthenticatedUserId, requireAuth } from '../middleware/require-auth';
import { isDuplicateUsernameError } from './user-registration.rules';

const router = Router();

function buildAuthResponse(user: {
  id: string;
  username: string;
  displayName: string;
}, token: string, expiresAt: number) {
  return {
    id: user.id,
    username: user.username,
    displayName: user.displayName,
    token,
    expiresAt
  };
}

router.post('/register', async (req, res) => {
  const { username, password, displayName } = req.body;

  if (!username || !password)
    return res.status(400).json({ error: 'Missing username/password' });

  const existing = await getUserByUsername(username);

  if (existing)
    return res.status(409).json({ error: 'Username taken' });

  const user = {
    id: uuidv4(),
    username,
    passwordHash: await hashPasswordForStorage(password),
    displayName: displayName || username,
    createdAt: Date.now()
  };

  try {
    await registerUser(user);
  } catch (error) {
    if (isDuplicateUsernameError(error)) {
      return res.status(409).json({ error: 'Username taken' });
    }

    throw error;
  }

  const session = await issueSessionToken(user.id);

  res.status(201).json(buildAuthResponse(user, session.token, session.expiresAt));
});

router.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await getUserByUsername(username);

  if (!user || !(await verifyPassword(password, user.passwordHash)))
    return res.status(401).json({ error: 'Invalid credentials' });

  const upgradedHash = await hashPasswordForStorage(password, user.passwordHash);

  if (upgradedHash !== user.passwordHash) {
    await updateUserPasswordHash(user.id, upgradedHash);
  }

  const session = await issueSessionToken(user.id);

  res.json(buildAuthResponse(user, session.token, session.expiresAt));
});

router.put('/me/signing-key', requireAuth, async (req, res) => {
  const { publicKeyJwk } = req.body;
  const userId = getAuthenticatedUserId(req);

  if (!publicKeyJwk || typeof publicKeyJwk !== 'object') {
    return res.status(400).json({ error: 'Missing publicKeyJwk', errorCode: 'INVALID_SIGNING_KEY' });
  }

  await updateUserSigningPublicKey(userId, JSON.stringify(publicKeyJwk));

  res.json({ ok: true });
});

router.get('/:id/signing-public-key', async (req, res) => {
  const user = await getUserById(req.params.id);

  if (!user?.signingPublicKey) {
    return res.status(404).json({ error: 'Signing public key not found', errorCode: 'SIGNING_KEY_NOT_FOUND' });
  }

  res.json({ publicKeyJwk: JSON.parse(user.signingPublicKey) });
});

router.post('/logout', requireAuth, async (req, res) => {
  if (req.authToken) {
    await revokeSessionToken(req.authToken);
  }

  res.json({ ok: true });
});

export default router;
