import { Router, Request, Response, NextFunction } from 'express';
import { AuthService } from '@/services/auth/auth.service.js';
import { AuthRequest, verifyToken } from '@/api/middlewares/auth.js';
import { TokenManager } from '@/infra/security/token.manager.js';
import { AppError } from '@/utils/errors.js';
import { successResponse } from '@/utils/response.js';
import {
  ADMIN_REFRESH_TOKEN_COOKIE_NAME,
  setAdminRefreshTokenCookie,
  clearAdminRefreshTokenCookie,
} from '@/utils/cookies.js';
import {
  ERROR_CODES,
  createAdminSessionRequestSchema,
  exchangeAdminSessionRequestSchema,
  type CreateAdminSessionResponse,
  type GetCurrentAdminSessionResponse,
} from '@insforge/shared-schemas';
import logger from '@/utils/logger.js';

const router = Router();
const authService = AuthService.getInstance();

// POST /api/auth/admin/sessions/exchange - Exchange authorization code for admin session
router.post('/sessions/exchange', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const validationResult = exchangeAdminSessionRequestSchema.safeParse(req.body);
    if (!validationResult.success) {
      throw new AppError(
        validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
        400,
        ERROR_CODES.INVALID_INPUT
      );
    }

    const { code } = validationResult.data;
    const result: CreateAdminSessionResponse =
      await authService.adminLoginWithAuthorizationCode(code);

    // Set refresh token as httpOnly cookie + CSRF token for web clients
    const tokenManager = TokenManager.getInstance();
    const { refreshToken, csrfToken } = tokenManager.generateRefreshTokenWithCsrf(
      result.admin.sub,
      'admin'
    );
    setAdminRefreshTokenCookie(res, refreshToken);

    successResponse(res, { ...result, csrfToken });
  } catch (error) {
    if (error instanceof AppError) {
      next(error);
    } else {
      logger.error('[Auth:AdminSessionExchange] Failed to exchange admin session', { error });
      next(new AppError('Failed to exchange admin session', 500, ERROR_CODES.INTERNAL_ERROR));
    }
  }
});

// POST /api/auth/admin/sessions - Create admin session (web only)
router.post('/sessions', (req: Request, res: Response, next: NextFunction) => {
  try {
    const validationResult = createAdminSessionRequestSchema.safeParse(req.body);
    if (!validationResult.success) {
      throw new AppError(
        validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
        400,
        ERROR_CODES.INVALID_INPUT
      );
    }

    const { username, password } = validationResult.data;
    const result: CreateAdminSessionResponse = authService.adminLogin(username, password);

    // Set refresh token as httpOnly cookie + CSRF token for web clients
    const tokenManager = TokenManager.getInstance();
    const { refreshToken, csrfToken } = tokenManager.generateRefreshTokenWithCsrf(
      result.admin.sub,
      'admin'
    );
    setAdminRefreshTokenCookie(res, refreshToken);

    successResponse(res, { ...result, csrfToken });
  } catch (error) {
    next(error);
  }
});

// GET /api/auth/admin/sessions/current - Get current dashboard admin session
router.get(
  '/sessions/current',
  verifyToken,
  (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      if (req.user?.role !== 'project_admin' || !req.user.id) {
        throw new AppError('Admin access required', 403, ERROR_CODES.AUTH_UNAUTHORIZED);
      }

      const response: GetCurrentAdminSessionResponse = {
        admin: {
          sub: req.user.id,
        },
      };

      successResponse(res, response);
    } catch (error) {
      next(error);
    }
  }
);

// POST /api/auth/admin/refresh - Refresh admin dashboard access token
// Uses a dashboard-specific httpOnly cookie + X-CSRF-Token header.
router.post('/refresh', (req: Request, res: Response, next: NextFunction) => {
  try {
    const tokenManager = TokenManager.getInstance();
    const refreshToken = req.cookies?.[ADMIN_REFRESH_TOKEN_COOKIE_NAME];

    if (!refreshToken) {
      throw new AppError('No admin refresh token provided', 401, ERROR_CODES.AUTH_UNAUTHORIZED);
    }

    const payload = tokenManager.verifyRefreshToken(refreshToken);
    if (payload.sessionType !== 'admin') {
      throw new AppError('Invalid admin refresh session type', 401, ERROR_CODES.AUTH_UNAUTHORIZED);
    }

    const csrfHeader = req.headers['x-csrf-token'] as string | undefined;
    if (!tokenManager.verifyCsrfToken(csrfHeader, payload)) {
      logger.warn('[Auth:AdminRefresh] CSRF token validation failed');
      throw new AppError('Invalid CSRF token', 403, ERROR_CODES.AUTH_UNAUTHORIZED);
    }

    const newAccessToken = tokenManager.generateAccessToken({
      sub: payload.sub,
      role: 'project_admin',
    });
    const { refreshToken: newRefreshToken, csrfToken: newCsrfToken } =
      tokenManager.generateRefreshTokenWithCsrf(payload.sub, 'admin', payload.csrfNonce);
    setAdminRefreshTokenCookie(res, newRefreshToken);

    successResponse(res, {
      admin: {
        sub: payload.sub,
      },
      accessToken: newAccessToken,
      csrfToken: newCsrfToken,
    });
  } catch (error) {
    if (error instanceof AppError && error.statusCode === 401) {
      clearAdminRefreshTokenCookie(res);
    }
    next(error);
  }
});

// POST /api/auth/admin/logout - Logout dashboard session
router.post('/logout', (_req: Request, res: Response, next: NextFunction) => {
  try {
    clearAdminRefreshTokenCookie(res);

    successResponse(res, {
      success: true,
      message: 'Logged out successfully',
    });
  } catch (error) {
    next(error);
  }
});

export default router;
