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';

/**
 * Panel Settings Controller
 *
 * This controller handles global panel settings and user preferences for the admin panel.
 * All endpoints are protected by SessionGuard to ensure only authenticated users can access them.
 *
 * Features include:
 * - User-specific table settings (column visibility, pagination)
 * - Menu item access control based on user permissions
 * - Timezone management for users
 * - Menu bookmarks (favorites) management
 * - Recently visited menus tracking
 *
 * @ApiTags('Global') - Groups all endpoints under 'Global' in Swagger UI
 * @UseGuards(SessionGuard) - Applies session-based authentication to all endpoints
 *
 * @example
 * // All endpoints require valid session token in Authorization header
 * Authorization: Bearer <session_token>
 */
@ApiTags('Global')
@UseGuards(SessionGuard)
@ApiBearerAuth() // Indicates that endpoints require Bearer token authentication
@Controller('') // Consider adding a prefix like 'panel-settings' or 'global'
export class PanelSettingsController {
  constructor(
    private readonly globalService: GlobalService,
    private readonly redisService: RedisService,
  ) {}

  /**
   * Retrieve user-specific table settings (column visibility, pagination preferences)
   *
   * This endpoint allows users to save and retrieve their personalized table configurations.
   * Settings are stored per user and can be applied to different tables in the application.
   *
   * @param body - Contains table identifier and desired settings
   * @param userData - Automatically injected user data from JWT/session token
   * @returns User's saved table settings or default settings
   *
   * @example
   * // POST /table-settings
   * // Request Body:
   * {
   *   "table_key": "user_listing",
   *   "columns": [
   *     { "key": "name", "title": "Name", "is_checked": true },
   *     { "key": "email", "title": "Email", "is_checked": true },
   *     { "key": "phone", "title": "Phone", "is_checked": false }
   *   ],
   *   "record_per_page": 25
   * }
   *
   * @example
   * // Response:
   * {
   *   "success": true,
   *   "data": {
   *     "table_key": "user_listing",
   *     "settings": { ... }
   *   }
   * }
   */
  @Post('table-settings')
  @ApiOperation({
    summary: 'Get or save table settings',
    description:
      'Retrieve or update user-specific table configurations (column visibility, pagination)',
  })
  @ApiResponse({
    status: 200,
    description: 'Table settings retrieved successfully',
  })
  @ApiResponse({ status: 401, description: 'Unauthorized - invalid session' })
  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 based on their permissions
   *
   * This endpoint returns a filtered list of menu items that the user has permission to access.
   * Used to dynamically build navigation menus in the admin panel.
   *
   * @param body - Optional filter parameters (e.g., menu type, category)
   * @param sessionData - Current session information including user permissions
   * @returns Array of menu items the user can access
   *
   * @example
   * // POST /my-menu-items
   * // Request Body:
   * {
   *   "permitted": true, // Only return items user has permission to access
   *   "menu_type": "sidebar" // Optional: filter by menu type
   * }
   */
  @Post('my-menu-items')
  @ApiOperation({
    summary: 'Get accessible menu items',
    description:
      'Retrieve menu items that the current user has permission to access',
  })
  @ApiResponse({
    status: 200,
    description: 'Menu items retrieved successfully',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - insufficient permissions',
  })
  async getUserAccessibleMenuItems(
    @Body(new ValidationPipe()) body: any,
    @SessionData() sessionData: SessionDataProps,
  ) {
    return await this.globalService.fetchUserAccessibleMenuItems(
      sessionData,
      body.permitted,
    );
  }

  /**
   * Get available timezones for the application
   *
   * This endpoint returns a list of all supported timezones that users can select from.
   * Typically used in user profile settings or application configuration.
   *
   * @param sessionData - Current session information
   * @returns List of timezone objects (id, name, offset, etc.)
   *
   * @example
   * // GET /timezones
   * // Response:
   * [
   *   { "id": "America/New_York", "name": "Eastern Time (ET)", "offset": "-05:00" },
   *   { "id": "Europe/London", "name": "Greenwich Mean Time (GMT)", "offset": "+00:00" },
   *   { "id": "Asia/Kolkata", "name": "India Standard Time (IST)", "offset": "+05:30" }
   * ]
   */
  @Get('timezones')
  @ApiOperation({
    summary: 'Get available timezones',
    description: 'Retrieve list of all supported timezones for user selection',
  })
  @ApiResponse({ status: 200, description: 'Timezones retrieved successfully' })
  async getTimezones(@SessionData() sessionData: SessionDataProps) {
    return await this.globalService.fetchTimezones(sessionData);
  }

  /**
   * Update user's timezone preference
   *
   * This endpoint updates the timezone setting for the current user's session.
   * The timezone is stored in Redis session data for real-time updates without
   * requiring database changes.
   *
   * @param updateTimezoneDto - New timezone selection
   * @param sessionData - Current session information
   * @returns Updated timezone data
   *
   * @example
   * // POST /update-timezone
   * // Request Body:
   * {
   *   "timezone": "America/New_York",
   *   "offset": "-05:00"
   * }
   */
  @Post('update-timezone')
  @ApiOperation({
    summary: 'Update user timezone',
    description: 'Update the timezone preference for the current session',
  })
  @ApiResponse({ status: 200, description: 'Timezone updated successfully' })
  async updateTimezone(
    @Body(new ValidationPipe()) updateTimezoneDto: UpdateTimezoneDto,
    @SessionData() sessionData: SessionDataProps,
  ) {
    if (sessionData.session_id) {
      // Retrieve current session data from Redis
      const data = await this.redisService.getSession(sessionData.session_id);

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

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

    return updateTimezoneDto;
  }

  /**
   * Manage menu bookmarks (favorites)
   *
   * This endpoint allows users to add/remove menu items from their bookmarks.
   * Bookmarked menus are typically displayed in a quick-access section of the UI.
   *
   * @param body - Bookmark operation (add/remove) and menu details
   * @param sessionData - Current session information
   * @returns Updated list of bookmarked menus
   *
   * @example
   * // POST /bookmark-menu
   * // Request Body:
   * {
   *   "menu_id": 123,
   *   "action": "add", // 'add' or 'remove'
   *   "menu_name": "User Management",
   *   "menu_url": "/users"
   * }
   */
  @Post('bookmark-menu')
  @ApiOperation({
    summary: 'Manage menu bookmarks',
    description: 'Add or remove menu items from user bookmarks/favorites',
  })
  @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,
      );

      // Update session data in Redis with new bookmarks
      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
   *
   * This endpoint tracks the user's recently accessed menu items for quick navigation.
   * Typically limited to the last 5-10 items and used to show a "recent" section in the UI.
   *
   * @param body - List of recently visited menu items
   * @param sessionData - Current session information
   * @returns Updated session data
   *
   * @example
   * // POST /recent-visited-menus
   * // Request Body:
   * {
   *   "recentVisitedMenus": [
   *     { "menu_id": 123, "menu_name": "Dashboard", "accessed_at": "2024-01-15T10:30:00Z" },
   *     { "menu_id": 456, "menu_name": "User List", "accessed_at": "2024-01-15T10:25:00Z" }
   *   ]
   * }
   */
  @Post('recent-visited-menus')
  @ApiOperation({
    summary: 'Update recently visited menus',
    description:
      "Track user's recently accessed menu items for quick navigation",
  })
  @ApiResponse({
    status: 200,
    description: 'Recent menus updated successfully',
  })
  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 visited menus in session data
        data.recentVisitedMenus = body.recentVisitedMenus;

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

      return data;
    }
  }
}
