import { logger } from '@goodgo/logger';
import { Request, Response } from 'express';

import { getErrorMessage } from '../../utils/error-utils';

import { oidcProviderService } from './oidc-provider.service';

/**
 * EN: OIDC Controller
 * VI: Controller OIDC
 */
export class OIDCController {
  /**
   * EN: OIDC Discovery endpoint
   * VI: Endpoint khám phá OIDC
   */
  async discovery(req: Request, res: Response): Promise<void> {
    try {
      const discovery = oidcProviderService.getDiscoveryDocument();
      res.json(discovery);
    } catch (error: unknown) {
      logger.error('OIDC discovery failed', { error });
      res.status(500).json({
        success: false,
        error: {
          code: 'OIDC_DISCOVERY_FAILED',
          message: 'Failed to get discovery document',
        },
      });
    }
  }

  /**
   * EN: OIDC Authorization endpoint
   * VI: Endpoint authorization OIDC
   */
  async authorize(req: Request, res: Response): Promise<void> {
    try {
      const { client_id, redirect_uri, scope, state } = req.query;

      if (!client_id || !redirect_uri) {
        res.status(400).json({
          success: false,
          error: {
            code: 'INVALID_REQUEST',
            message: 'client_id and redirect_uri are required',
          },
        });
        return;
      }

      // Generate authorization code
      const userId = (req as any).user?.id || (req as any).user?.sub;
      if (!userId) {
        // Redirect to login
        res.redirect(`/auth/login?redirect=${encodeURIComponent(req.originalUrl)}`);
        return;
      }

      const code = await oidcProviderService.generateAuthorizationCode(
        client_id as string,
        userId,
        redirect_uri as string,
        (scope as string)?.split(' ') || ['openid', 'email', 'profile']
      );

      // Redirect with code
      const redirectUrl = new URL(redirect_uri as string);
      redirectUrl.searchParams.set('code', code);
      if (state) {
        redirectUrl.searchParams.set('state', state as string);
      }

      res.redirect(redirectUrl.toString());
    } catch (error: unknown) {
      logger.error('OIDC authorization failed', { error });
      res.status(500).json({
        success: false,
        error: {
          code: 'OIDC_AUTHORIZATION_FAILED',
          message: 'Authorization failed',
        },
      });
    }
  }

  /**
   * EN: OIDC Token endpoint
   * VI: Endpoint token OIDC
   */
  async token(req: Request, res: Response): Promise<void> {
    try {
      const { code, client_id, client_secret, redirect_uri, grant_type } = req.body;

      if (grant_type !== 'authorization_code') {
        res.status(400).json({
          success: false,
          error: {
            code: 'UNSUPPORTED_GRANT_TYPE',
            message: 'Only authorization_code grant type is supported',
          },
        });
        return;
      }

      const tokens = await oidcProviderService.exchangeCodeForTokens(
        code,
        client_id,
        client_secret,
        redirect_uri
      );

      res.json({
        access_token: tokens.accessToken,
        refresh_token: tokens.refreshToken,
        id_token: tokens.idToken,
        token_type: 'Bearer',
        expires_in: 900, // 15 minutes
      });
    } catch (error: unknown) {
      logger.error('OIDC token exchange failed', { error });
      res.status(400).json({
        success: false,
        error: {
          code: 'TOKEN_EXCHANGE_FAILED',
          message: getErrorMessage(error) || 'Token exchange failed',
        },
      });
    }
  }

  /**
   * EN: OIDC UserInfo endpoint
   * VI: Endpoint UserInfo OIDC
   */
  async userinfo(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.id || (req as any).user?.sub;
      if (!userId) {
        res.status(401).json({
          success: false,
          error: {
            code: 'UNAUTHORIZED',
            message: 'Authentication required',
          },
        });
        return;
      }

      // Get user info from database
      const { getPrismaClient } = await import('../../config/database.config');
      const prisma = getPrismaClient();
      const user = await prisma.user.findUnique({
        where: { id: userId },
      });

      if (!user) {
        res.status(404).json({
          success: false,
          error: {
            code: 'USER_NOT_FOUND',
            message: 'User not found',
          },
        });
        return;
      }

      res.json({
        sub: user.id,
        email: user.email,
        email_verified: user.emailVerified,
        name: user.username,
        updated_at: Math.floor(user.updatedAt.getTime() / 1000),
      });
    } catch (error: unknown) {
      logger.error('OIDC userinfo failed', { error });
      res.status(500).json({
        success: false,
        error: {
          code: 'USERINFO_FAILED',
          message: 'Failed to get user info',
        },
      });
    }
  }

  /**
   * EN: OIDC JWKS endpoint
   * VI: Endpoint JWKS OIDC
   */
  async jwks(req: Request, res: Response): Promise<void> {
    try {
      const jwks = oidcProviderService.getJWKS();
      res.json(jwks);
    } catch (error: unknown) {
      logger.error('OIDC JWKS failed', { error });
      res.status(500).json({
        success: false,
        error: {
          code: 'JWKS_FAILED',
          message: 'Failed to get JWKS',
        },
      });
    }
  }
}

export const oidcController = new OIDCController();
