import {
  Body,
  Controller,
  Get,
  Post,
  UseGuards,
  ValidationPipe,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiBearerAuth,
  ApiResponse,
} from '@nestjs/swagger';
import { SessionData, SessionDataProps } from 'src/utils/decorators';
import { SessionGuard } from 'src/config/guards/session.guard';
import {
  UserData,
  UserDataProps,
} from 'src/utils/decorators/user-data.decorator';
import { RedisService } from '../../redis/redis.service';
import { GlobalService } from '../global.service';
import { TableSettingsDto } from '../dto/table-settings-dto';
import { UpdateTimezoneDto } from '../dto/update-timezone.dto';
import { CreateMenuBookmarkDto } from '../dto/create-menu-bookmark.dto';

/**
 * Table Settings Controller
 *
 * NOTE: This controller appears to be identical to PanelSettingsController.
 * Consider consolidating if both exist in the same codebase.
 *
 * This controller handles user preferences and global settings for the admin panel:
 * - Personalized table configurations (column visibility, pagination)
 * - Role-based menu access control
 * - Timezone management
 * - Menu bookmarks (favorites)
 * - Recently visited menu tracking
 *
 * All endpoints are protected by SessionGuard which validates user sessions
 * stored in Redis. The controller uses custom decorators (@SessionData, @UserData)
 * to inject user and session information into handler methods.
 *
 * @ApiTags('Global') - Groups endpoints under 'Global' in Swagger documentation
 * @UseGuards(SessionGuard) - Requires valid session for all endpoints
 *
 * @example
 * // All requests require session authentication
 * // Headers: Authorization: Bearer <session_token>
 */
@ApiTags('Global')
@UseGuards(SessionGuard)
@ApiBearerAuth() // Specifies Bearer token authentication for Swagger
@Controller('') // NOTE: Empty path - consider renaming controller or adding path prefix
export class TableSettingsController {
  /**
   * Constructor with dependency injection
   * @param globalService - Service for global/user preference operations
   * @param redisService - Service for Redis session management
   */
  constructor(
    private readonly globalService: GlobalService,
    private readonly redisService: RedisService,
  ) {}

  /**
   * Retrieve or save user-specific table settings
   *
   * Allows users to customize table displays by showing/hiding columns
   * and setting pagination preferences. Settings are persisted per user
   * and can be applied to different tables across the application.
   *
   * @param body - Table settings configuration
   * @param userData - Injected user information from session
   * @returns Saved table settings or default configuration
   *
   * @example
   * // POST /table-settings
   * {
   *   "table_key": "student_list",
   *   "columns": [
   *     { "key": "name", "title": "Student Name", "is_checked": true },
   *     { "key": "course", "title": "Course", "is_checked": true },
   *     { "key": "status", "title": "Status", "is_checked": false }
   *   ],
   *   "record_per_page": 50
   * }
   */
  @Post('table-settings')
  @ApiOperation({
    summary: 'Manage table settings',
    description: 'Save or retrieve user-specific table configurations',
  })
  @ApiResponse({
    status: 200,
    description: 'Table settings processed successfully',
  })
  async findTableSettings(
    @Body(new ValidationPipe()) body: TableSettingsDto,
    @UserData() userData: UserDataProps,
  ) {
    return await this.globalService.getTableSettings(body, userData.id);
  }

  /**
   * Get menu items accessible to the current user
   *
   * Filters menu items based on user roles and permissions.
   * Used to dynamically build navigation menus that only show
   * items the user is authorized to access.
   *
   * @param body - Filter parameters (e.g., permitted-only flag)
   * @param sessionData - Current session with user permissions
   * @returns Filtered list of accessible menu items
   *
   * @example
   * // POST /my-menu-items
   * {
   *   "permitted": true // Return only items user has permission to access
   * }
   */
  @Post('my-menu-items')
  @ApiOperation({
    summary: 'Get accessible menu items',
    description: 'Retrieve menu items based on user permissions',
  })
  @ApiResponse({
    status: 200,
    description: 'Accessible menu items retrieved',
  })
  async getUserAccessibleMenuItems(
    @Body(new ValidationPipe()) body: any,
    @SessionData() sessionData: SessionDataProps,
  ) {
    return await this.globalService.fetchUserAccessibleMenuItems(
      sessionData,
      body.permitted,
    );
  }

  /**
   * Retrieve available timezone options
   *
   * Returns a comprehensive list of timezones for user selection.
   * Typically used in user profile or application settings.
   *
   * @param sessionData - Current session information
   * @returns Array of timezone objects with IDs and display names
   *
   * @example
   * // GET /timezones
   * // Response:
   * [
   *   { "value": "America/New_York", "label": "Eastern Time (ET)" },
   *   { "value": "UTC", "label": "Coordinated Universal Time" },
   *   { "value": "Asia/Kolkata", "label": "India Standard Time" }
   * ]
   */
  @Get('timezones')
  @ApiOperation({
    summary: 'Get timezone list',
    description: 'Retrieve all available timezones for selection',
  })
  @ApiResponse({
    status: 200,
    description: 'Timezone list retrieved',
  })
  async getTimezones(@SessionData() sessionData: SessionDataProps) {
    return await this.globalService.fetchTimezones(sessionData);
  }

  /**
   * Update user's timezone preference
   *
   * Updates the timezone setting in the user's current session.
   * The change is immediately reflected in session data but may
   * require a separate endpoint to persist to user profile.
   *
   * @param updateTimezoneDto - New timezone selection
   * @param sessionData - Current session with session ID
   * @returns Updated timezone data
   *
   * @example
   * // POST /update-timezone
   * {
   *   "timezone": "Europe/London",
   *   "gmt_offset": "+00:00"
   * }
   */
  @Post('update-timezone')
  @ApiOperation({
    summary: 'Update session timezone',
    description: 'Change timezone preference for current session',
  })
  @ApiResponse({
    status: 200,
    description: 'Timezone updated in session',
  })
  async updateTimezone(
    @Body(new ValidationPipe()) updateTimezoneDto: UpdateTimezoneDto,
    @SessionData() sessionData: SessionDataProps,
  ) {
    if (sessionData.session_id) {
      // Retrieve current session from Redis
      const data = await this.redisService.getSession(sessionData.session_id);

      if (data) {
        // Update timezone in session data
        data.timezone = updateTimezoneDto;

        // Persist updated session back to Redis
        await this.redisService.setSession(sessionData.session_id, data);
      }
    }

    return updateTimezoneDto;
  }

  /**
   * Add or remove menu bookmarks
   *
   * Allows users to bookmark frequently accessed menu items
   * for quick navigation. Bookmarks are stored per user and
   * synchronized with the session data.
   *
   * @param body - Bookmark operation details
   * @param sessionData - Current session information
   * @returns Updated bookmarks list
   *
   * @example
   * // POST /bookmark-menu
   * {
   *   "menu_id": 42,
   *   "menu_name": "Analytics Dashboard",
   *   "action": "add" // 'add' or 'remove'
   * }
   */
  @Post('bookmark-menu')
  @ApiOperation({
    summary: 'Manage menu bookmarks',
    description: 'Add or remove menu items from user bookmarks',
  })
  @ApiResponse({
    status: 200,
    description: 'Bookmarks updated successfully',
  })
  async manageBookmarkMenu(
    @Body(new ValidationPipe()) body: CreateMenuBookmarkDto,
    @SessionData() sessionData: SessionDataProps,
  ) {
    if (sessionData.session_id) {
      // Update bookmarks in database
      const bookmarks = await this.globalService.manageBookmarkMenu(
        sessionData.user.id,
        body,
      );

      // Synchronize bookmarks with session data
      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 within the application.
   * Typically used to show "Recently Accessed" quick links.
   * History is stored in session and may have size limits.
   *
   * @param body - List of recently visited menu items
   * @param sessionData - Current session information
   * @returns Updated session data
   *
   * @example
   * // POST /recent-visited-menus
   * {
   *   "recentVisitedMenus": [
   *     {
   *       "menu_id": 1,
   *       "menu_name": "Dashboard",
   *       "visited_at": "2024-01-15T10:30:00Z"
   *     }
   *   ]
   * }
   */
  @Post('recent-visited-menus')
  @ApiOperation({
    summary: 'Update recent menu visits',
    description: 'Track recently accessed menu items for quick navigation',
  })
  @ApiResponse({
    status: 200,
    description: 'Recent menus updated',
  })
  async recentVisitedMenus(
    @Body(new ValidationPipe()) body: Record<string, any>,
    @SessionData() sessionData: SessionDataProps,
  ) {
    if (sessionData.session_id) {
      const data = await this.redisService.getSession(sessionData.session_id);

      if (data) {
        // Update recent visits in session
        data.recentVisitedMenus = body.recentVisitedMenus;

        // Persist updated session
        await this.redisService.setSession(sessionData.session_id, data);
      }

      return data;
    }
  }
}
