import {
  Body,
  Controller,
  Get,
  Post,
  Req,
  UnauthorizedException,
  UseGuards,
  ValidationPipe,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
} from '@nestjs/swagger';
import { GlobalService } from './global.service';
import { TableSettingsDto } from './dto/table-settings-dto';
import { SessionData, SessionDataProps } from 'src/utils/decorators';
import { RedisService } from '../redis/redis.service';
import { SessionGuard } from 'src/config/guards/session.guard';
import { UpdateTimezoneDto } from './dto/update-timezone.dto';
import { CreateMenuBookmarkDto } from './dto/create-menu-bookmark.dto';
import {
  UserData,
  UserDataProps,
  UserDataType,
} from 'src/utils/decorators/user-data.decorator';
import * as crypto from 'crypto';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { tokenInterface } from 'src/modules/auth/interfaces/tokenInteface';
import { DEFAULT_USER_ID, jwtConstants } from 'src/constants/auth';
import { AuthGuard } from 'src/modules/auth/auth.guard';
import { handleServiceRequest } from 'src/utils/response/response-handler';
import { VisitedMenuItemDto } from './dto/visited-menus.dto';

/**
 * Global Public Controller
 *
 * This controller handles public endpoints that don't require authentication.
 * Currently only contains session validation endpoint.
 *
 * @ApiTags('Global') - Groups endpoints under 'Global' in Swagger
 * @Controller('') - Empty path, endpoints are at root level
 *
 * Note: Consider moving public endpoints to a separate controller with
 * a different path prefix like '/public' for better organization.
 */
@ApiTags('Global')
@Controller('') // Consider adding path: @Controller('public')
export class GlobalPublicController {
  constructor(
    private readonly redisService: RedisService,
    private readonly jwtService: JwtService,
    private readonly config: ConfigService,
    private readonly globalService: GlobalService,
  ) {}

  /**
   * Validates a user session by checking if it exists in Redis.
   *
   * This is a public endpoint used by clients to check if their session
   * is still valid without triggering authentication errors. Typically
   * called during app initialization or periodic session checks.
   *
   * @param body - Should contain session_id
   * @returns Session profile if valid, throws error if session not found
   *
   * @example
   * // POST /validate-session
   * // Request:
   * { "session_id": "abc123def456" }
   *
   * // Response (success):
   * {
   *   "user": { "id": 123, "email": "user@example.com", ... },
   *   "timezone": { ... },
   *   "session_id": "abc123def456",
   *   // ... other session data
   * }
   *
   * // Response (error):
   * // Throws Error: 'Session not found'
   *
   * @security Note: This endpoint is public but should be rate-limited
   * to prevent session enumeration attacks.
   */
  @Post('validate-session')
  // @UseGuards(SessionGuard)
  @ApiOperation({
    summary: 'Validate user session',
    description: 'Check if a session ID is still valid and return session data',
  })
  @ApiResponse({ status: 200, description: 'Session is valid' })
  @ApiResponse({ status: 404, description: 'Session not found' })
  async validateSession(@Body() body: any, @UserData() userData: UserDataType) {
    const userInfo = await handleServiceRequest(
      // this.globalService.getUserInfo(userData.id),
      this.globalService.getUser(DEFAULT_USER_ID),
    );
    // const userInfo = await this.globalService.getUserInfo(userData.id);

    const user = {
      ...userInfo,
      role_id: '1',//userInfo.user_roles[0]?.role.id,
      role_permissions: [],
      menu_type: 1,
    };

    const result = {
      user,
      session_id: 'req.sessionID',
      panelTheme: {},
      timezone: {},
      menuBookmarks: [],
      menu_type: user?.menu_type || '1',
      recentVisitedMenus: []
    };

    if (result?.user) {
      // delete result.user.favorite_menu;
      // delete result.user.theme;
      // delete result.user.recent_visited_menus;
    }

    return result;
  }

  /**
   * Generates both access and refresh tokens for authenticated user
   *
   * Creates a token pair for maintaining user sessions:
   * - Access token: Short-lived token for API authorization
   * - Refresh token: Long-lived token for obtaining new access tokens
   *
   * @param user - Authenticated user entity
   * @returns Token interface containing both tokens and expiration
   *
   * @note Tokens are generated in parallel for performance
   * @note Refresh token expiration is configured separately
   *
   * @example
   * const tokens = await authService.generateToken(user);
   * // Returns: { access_token: '...', refresh_token: '...', expires_in: 3600 }
   */
  async generateToken(user: any): Promise<tokenInterface> {
    // Generate tokens in parallel for better performance
    const [access_token, refresh_token] = await Promise.all([
      this.getToken(user),
      this.getToken(user, true),
    ]);

    return {
      access_token,
      refresh_token,
      expires_in: jwtConstants.refreshtokenexpirarytime,
    };
  }

  /**
   * Private helper to generate JWT tokens
   *
   * Creates signed JWT tokens with user claims and configurable expiration.
   * Can generate either access tokens or refresh tokens based on flag.
   *
   * @param user - User entity for token claims
   * @param is_refresh - Flag indicating if generating refresh token (default: false)
   * @returns Signed JWT token string
   *
   * @note Token claims include minimal required data (sub, email, role)
   * @note Uses different expiration times for access vs refresh tokens
   * @note JWT secret is loaded from environment configuration
   */
  private async getToken(user: any, is_refresh = false): Promise<string> {
    // Prepare token payload with user claims
    const payload = {
      sub: user.id ?? (user as any).sub, // Support both UserEntity and possibly other user objects
      email: user.email ?? (user as any).fname,
      role: '2', // Hardcoded role - consider making this dynamic based on user.role
    };

    // Configure token expiration based on type
    const expiresIn = is_refresh
      ? jwtConstants.refreshtokenexpirary
      : jwtConstants.accesstokenexpirary;

    // Sign and return the JWT token
    return this.jwtService.signAsync(payload, {
      secret: this.config.get('JWT_SECRET'),
      expiresIn: expiresIn,
    });
  }
}

/**
 * Global Secure Controller
 *
 * This controller handles all authenticated endpoints for global/user settings.
 * All endpoints are protected by SessionGuard which validates the user's
 * session token before allowing access.
 *
 * Features include:
 * - User table settings management
 * - Menu access control
 * - Timezone management
 * - Menu bookmarks
 * - Recent visits tracking
 *
 * @ApiTags('Global') - Same tag as public controller (consider separating)
 * @UseGuards(SessionGuard) - Requires valid session for all endpoints
 * @Controller('') - Empty path, endpoints are at root level
 *
 * Note: Having both public and secure controllers in same module with same
 * path prefix can be confusing. Consider reorganizing.
 */
@ApiTags('Global')
// @UseGuards(SessionGuard)
@ApiBearerAuth() // Indicates endpoints require Bearer token
@Controller('') // Consider: @Controller('secure') or @Controller('settings')
export class GlobalSecureController {
  constructor(
    private readonly globalService: GlobalService,
    private readonly redisService: RedisService,
    private readonly jwtService: JwtService,

    private readonly configService: ConfigService,
  ) {}

  /**
   * Get or save user-specific table settings.
   *
   * This endpoint manages personalized table configurations including
   * column visibility and pagination preferences.
   *
   * @param body - Table settings parameters
   * @param userData - Injected user data from session token
   * @returns User's table settings
   *
   * @note Missing ValidationPipe on body parameter - consider adding it
   */
  @Post('table-settings')
  // @UseGuards(SessionGuard)
  @ApiOperation({
    summary: 'Get or save table settings',
    description:
      'Manage user-specific table configurations (columns, pagination)',
  })
  @ApiResponse({ status: 200, description: 'Table settings retrieved/saved' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async findTableSettings(
    @Body(new ValidationPipe()) body: TableSettingsDto,
    // @UserData() userData: UserDataType,
  ) {
    // console.log("userData:", userData);
    return await this.globalService.getTableSettings(body, DEFAULT_USER_ID);
  }

  /**
   * Get menu items accessible to the current user.
   *
   * Filters menu items based on user's permissions to build
   * a personalized navigation menu.
   *
   * @param body - Should contain 'permitted' flag
   * @param sessionData - Current session data with user permissions
   * @returns Filtered menu items
   *
   * @note Missing ValidationPipe on body parameter
   */
  @Post('my-menu-items')
  @ApiOperation({
    summary: 'Get accessible menu items',
    description:
      'Retrieve menu items the current user has permission to access',
  })
  @ApiResponse({ status: 200, description: 'Menu items retrieved' })
  @ApiResponse({ status: 403, description: 'Insufficient permissions' })
  async getUserAccessibleMenuItems(
    @Body() body: any, // Missing ValidationPipe and type definition
    @SessionData() sessionData: SessionDataProps,
  ) {
    return await this.globalService.fetchUserAccessibleMenuItems(
      sessionData,
      body.permitted,
    );
  }

  /**
   * Get available timezones.
   *
   * Returns list of all supported timezones for user selection.
   * Typically used in user profile or settings pages.
   *
   * @param sessionData - Current session data
   * @returns Array of timezone objects
   */
  @Get('timezones')
  @ApiOperation({
    summary: 'Get timezone list',
    description: 'Retrieve all available timezones for selection',
  })
  @ApiResponse({ status: 200, description: 'Timezones retrieved' })
  async getTimezones(@SessionData() sessionData: SessionDataProps) {
    return await this.globalService.fetchTimezones(sessionData);
  }

  /**
   * Update user's timezone preference.
   *
   * Updates timezone in the current session data stored in Redis.
   * Changes take effect immediately for the current session.
   *
   * @param updateTimezoneDto - New timezone data
   * @param sessionData - Current session with session_id
   * @returns Updated timezone data
   */
  @Post('update-timezone')
  @ApiOperation({
    summary: 'Update timezone',
    description: 'Update timezone preference for current session',
  })
  @ApiResponse({ status: 200, description: 'Timezone updated' })
  async updateTimezone(
    @Body(new ValidationPipe()) updateTimezoneDto: UpdateTimezoneDto,
    @UserData() UserData: UserDataProps,
  ) {
    await this.globalService.updateUser(UserData.id, {
      user_timezone: updateTimezoneDto,
    });

    return updateTimezoneDto;
  }

  /**
   * Manage menu bookmarks (favorites).
   *
   * Adds or removes menu items from user's bookmarks.
   * Updates both database and session cache.
   *
   * @param body - Bookmark operation details
   * @param sessionData - Current session data
   * @returns Updated bookmarks list
   */
  @Post('bookmark-menu')
  @ApiOperation({
    summary: 'Manage menu bookmarks',
    description: 'Add or remove menu items from user bookmarks',
  })
  @ApiResponse({ status: 200, description: 'Bookmarks updated' })
  async manageBookmarkMenu(
    @Body(new ValidationPipe()) body: CreateMenuBookmarkDto,
    @UserData() userData: UserDataProps,
  ) {
    if (userData.id) {
      const bookmarks = await this.globalService.manageBookmarkMenu(
        userData.id,
        body,
      );

      // const data = await this.redisService.getSession(sessionData.session_id);
      // if (data) {
      //   data.menuBookmarks = bookmarks;
      //   await this.redisService.setSession(sessionData.session_id, data);
      // }

      return bookmarks;
    }
  }

  /**
   * Update recently visited menus.
   *
   * Tracks user's navigation history for quick access.
   * History is stored in session cache.
   *
   * @param body - Should contain 'recentVisitedMenus' array
   * @param sessionData - Current session data
   * @returns Updated session data
   *
   * @note Missing ValidationPipe on body parameter
   */
  @Post('recent-visited-menus')
  @ApiOperation({
    summary: 'Update recent menu visits',
    description: 'Track recently accessed menu items',
  })
  @ApiResponse({ status: 200, description: 'Recent visits updated' })
  async recentVisitedMenus(
    @Body() body: VisitedMenuItemDto, // Missing ValidationPipe
    @UserData() userData: UserDataProps,
  ) {
    // console.log('body', userData, body);
    return await this.globalService.updateRecentVisitedMenus(
      userData.id,
      body,
    );
    // if (sessionData.session_id) {
    //   const data = await this.redisService.getSession(sessionData.session_id);
    //   if (data) {
    //     data.recentVisitedMenus = body.recentVisitedMenus;
    //     await this.redisService.setSession(sessionData.session_id, data);
    //   }
    //   return data;
    // }
  }
}
