import {
  Controller,
  Post,
  Body,
  Req,
  Res,
  UseGuards,
  HttpCode,
  Logger,
  HttpException,
  HttpStatus,
  Get,
  Param,
  InternalServerErrorException,
} from '@nestjs/common';
import { Response } from 'express';
import { AuthService } from './auth.service';
// import { IUser } from '../user/interface/users.interface';
import { JwtAuthGuard } from 'common/guards/jwt-auth.guard';
import { ApiOperation, ApiResponse, ApiTags, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { CreateUserDto } from 'modules/user/dto/create-user.dto';
import { UserResponseDto } from 'modules/user/dto/user-response.dto';
import { UserService } from 'modules/user/users.service';
import { plainToInstance } from 'class-transformer';
import { AuthGuard } from '@nestjs/passport';
import { ConfigService } from '@nestjs/config';
import { UserRole } from 'common/enum/UserRole.enum';
import { CreateAdminDto } from 'modules/user/dto/create-admin.dto';
import { UserLoginDto } from './dtos/user-login.dto';
import { ChangePasswordDto, ResetPasswordByEmailDto } from './dtos/change-password.dto';
import { VerifyEmailDto } from './dtos/verify-email.dto';

@ApiTags('Authentication')
@Controller({ path: 'auth', version: '1' })
export class AuthController {
  private readonly logger = new Logger(AuthController.name);
  constructor(
    private readonly configService: ConfigService,
    private readonly authService: AuthService,
    private readonly userService: UserService,
  ) {}

  @Get('google')
  @ApiOperation({ summary: 'Google OAuth login' })
  async googleAuth(@Req() req: any, @Res() res: Response) {
    try {
      // Get origin from referer or origin header
      const origin = req.headers.referer || req.headers.origin;
      this.logger.debug('Origin detected:', origin);

      // Create state parameter with origin information
      const state = origin ? Buffer.from(JSON.stringify({ origin })).toString('base64') : undefined;
      this.logger.debug('State:', state);
      // Get login URL with state parameter
      const loginUrl = await this.authService.googleLoginUrl(state);
      this.logger.debug('Redirecting to:', loginUrl.url);
      return res.redirect(loginUrl.url);
    } catch (error) {
      this.logger.error('Error during Google login:', error);
      throw new HttpException(error.message, HttpStatus.UNAUTHORIZED);
    }
  }

  @Get('google/callback')
  @ApiOperation({ summary: 'Google OAuth callback' })
  @UseGuards(AuthGuard('google'))
  async googleAuthCallback(@Req() req: any, @Res() res: Response) {
    try {
      this.logger.debug('Google callback received', { user: req.user });

      const authResponse = await this.authService.googleLogin(req.user);
      const response = {
        message: 'Login successful',
        authToken: authResponse.cookie,
        user: plainToInstance(UserResponseDto, authResponse.user),
      };

      res.setHeader('Set-Cookie', authResponse.cookie);

      const isProduction = this.configService.get('NODE_ENV') === 'production';

      // Get the allowed CORS origins and split into an array
      const clientUrls = (isProduction ? this.configService.get<string>('CORS_ORIGIN_PROD') : this.configService.get<string>('CORS_ORIGIN_DEV'))
        .split(',')
        .map(url => url.trim())
        .filter(url => url.length > 0);

      if (clientUrls.length === 0) {
        throw new Error('No valid client URLs found in CORS_ORIGIN.');
      }

      // Use the origin from the state if available
      let redirectBaseUrl: string | undefined;

      if (req.user.origin && req.user.origin.origin) {
        const originFromState = req.user.origin.origin;
        this.logger.debug('Origin from state:', originFromState);

        // Find if this origin is in our allowed list
        redirectBaseUrl = clientUrls.find(url => originFromState.startsWith(url));

        if (redirectBaseUrl) {
          this.logger.debug('Found matching allowed origin:', redirectBaseUrl);
        } else {
          this.logger.warn('Origin from state not in allowed list, using default');
        }
      }

      // Fall back to first allowed URL if no valid origin was found
      if (!redirectBaseUrl) {
        redirectBaseUrl = clientUrls[0];
        this.logger.debug('Using default redirect URL:', redirectBaseUrl);
      }

      const redirectUrl = new URL(`${redirectBaseUrl}/auth/callback`);
      redirectUrl.searchParams.append('user', JSON.stringify(response));

      this.logger.debug('Redirecting to:', redirectUrl.toString());

      return res.redirect(redirectUrl.toString());
    } catch (error) {
      this.logger.error('Google OAuth Error:', {
        message: error.message,
        stack: error.stack,
        fullError: error,
      });

      throw new HttpException(
        {
          statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
          error: error.message || 'Something went wrong',
          details: error.stack, // Include stack trace
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Get('logout')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'User logout' })
  @ApiBearerAuth()
  @ApiResponse({ status: 200, description: 'Logout successful' })
  @HttpCode(200)
  async logOut(@Req() req: any, @Res() res: Response) {
    const { user } = req;
    this.logger.log(`User logout attempt: ${user.email}`);
    if (!user) {
      throw new HttpException('User not found', HttpStatus.NOT_FOUND);
    }

    res.setHeader('Set-Cookie', [
      'Authentication=; HttpOnly; Path=/; Max-Age=0',
      'refreshToken=; HttpOnly; Path=/; Max-Age=0',
      'session=; HttpOnly; Path=/; Max-Age=0',
    ]);
    return res.json({ message: 'Logout successful' });
  }

  @Post('client/register')
  @ApiOperation({ summary: 'Register a new user' })
  @ApiBody({ type: CreateUserDto })
  @ApiResponse({ status: 201, description: 'User registered successfully', type: UserResponseDto })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 409, description: 'Email already exists' })
  async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
    const user = await this.authService.createUser(createUserDto);
    return user;
  }

  @Post('admin/register')
  @ApiOperation({ summary: 'Register a new admin' })
  @ApiBody({ type: CreateAdminDto })
  @ApiResponse({ status: 201, description: 'Admin registered successfully', type: UserResponseDto })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 409, description: 'Email already exists' })
  async createAdmin(@Body() createUserDto: CreateAdminDto): Promise<UserResponseDto> {
    this.logger.log('Admin registration attempt', { email: createUserDto });
    if (createUserDto.role != UserRole.ADMIN) {
      throw new HttpException('Invalid role', HttpStatus.BAD_REQUEST);
    }
    this.logger.log('Admin registration attempt', { email: createUserDto.email });
    const user = await this.userService.createAdmin(createUserDto);
    return user;
  }

  @Post('client/login')
  @ApiOperation({ summary: 'User login' })
  @ApiBody({ type: UserLoginDto })
  @ApiResponse({ status: 200, description: 'Login successful', type: UserResponseDto })
  @ApiResponse({ status: 401, description: 'Unauthorized - Invalid credentials' })
  async login(@Body() userData: UserLoginDto, @Res() response: Response) {
    try {
      const { cookieData, token, findUser } = await this.authService.login(userData);

      // Set the cookie using res.cookie()
      response.cookie(cookieData.name, cookieData.value, cookieData.options);

      return response.json({
        message: 'Login successful',
        authToken: token,
        user: findUser,
      });
    } catch (error) {
      this.logger.error('Error during login:', error);
      throw new HttpException(error.message, error.status || HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @Post('admin/login')
  @ApiOperation({ summary: 'Admin login' })
  @ApiBody({
    type: UserLoginDto,
    description: 'Admin login credentials',
  })
  @ApiResponse({
    status: 200,
    description: 'Login successful',
    schema: {
      type: 'object',
      properties: { message: { type: 'string' }, authToken: { type: 'string' }, user: { $ref: '#/components/schemas/UserResponseDto' } },
    },
  })
  @ApiResponse({ status: 401, description: 'Unauthorized - Invalid credentials' })
  async adminLogin(@Body() body: UserLoginDto, @Res() response: Response) {
    this.logger.log(`Admin login attempt: ${body.email}`);
    try {
      const { cookieData, token, findUser } = await this.authService.adminLogin(body);

      // Set the cookie using res.cookie()
      response.cookie(cookieData.name, cookieData.value, cookieData.options);

      return response.json({
        message: 'Admin login successful',
        authToken: token,
        user: plainToInstance(UserResponseDto, findUser),
      });
    } catch (error) {
      this.logger.error('Error during admin login:', error);
      throw new HttpException(error.message, error.status || HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @Post('change-password/:userId')
  @ApiOperation({ summary: 'Change user password' })
  @ApiBody({
    type: ChangePasswordDto,
    description: 'Change password request',
  })
  @ApiResponse({
    status: 200,
    description: 'Password changed successfully',
    schema: {
      type: 'object',
      properties: {
        message: { type: 'string', example: 'Password changed successfully' },
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid input' })
  @ApiResponse({ status: 401, description: 'Unauthorized - Invalid credentials' })
  @ApiResponse({ status: 404, description: 'User not found' })
  @UseGuards(JwtAuthGuard)
  async changePassword(
    @Param('userId') userId: string,
    @Req() req: any,
    @Body() body: ChangePasswordDto,
  ): Promise<{ message: string; user: UserResponseDto }> {
    this.logger.log(`Change password request for user ID: ${userId}`);
    const user = req.user;

    if (!user || user.id !== parseInt(userId, 10)) {
      throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED);
    }

    try {
      const updatedUser = await this.authService.ChangeUserPassword(parseInt(userId), body.oldPassword, body.newPassword);

      return {
        message: 'Password changed successfully',
        user: plainToInstance(UserResponseDto, updatedUser),
      };
    } catch (error) {
      this.logger.error('Error changing password:', error);
      if (error instanceof HttpException) throw error;
      throw new InternalServerErrorException('Failed to change password');
    }
  }

  // reset password
  @Post('reset-password/:userId')
  @ApiOperation({ summary: 'Reset user password' })
  @ApiBody({
    type: ResetPasswordByEmailDto,
    description: 'Reset password request',
  })
  @ApiResponse({
    status: 200,
    description: 'Password reset successfully',
    schema: {
      type: 'object',
      properties: {
        message: { type: 'string', example: 'Password reset successfully' },
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid input' })
  @ApiResponse({ status: 401, description: 'Unauthorized - Invalid credentials' })
  @ApiResponse({ status: 404, description: 'User not found' })
  async resetPasswordByEmail(@Body() body: ResetPasswordByEmailDto): Promise<{ message: string; user: UserResponseDto }> {
    this.logger.log(`Reset password request for user email: ${body.email}`);
    const { email, newPassword } = body;

    if (!email || !newPassword) {
      throw new HttpException('Email and new password are required', HttpStatus.BAD_REQUEST);
    }

    try {
      const updatedUser = await this.authService.resetPasswordByEmail(email, newPassword);

      return {
        message: 'Password reset successfully',
        user: plainToInstance(UserResponseDto, updatedUser),
      };
    } catch (error) {
      this.logger.error('Error resetting password:', error);
      if (error instanceof HttpException) throw error;
      throw new InternalServerErrorException('Failed to reset password');
    }
  }

  @Post('verify-email')
  @ApiOperation({ summary: 'Verify user email' })
  @ApiBody({
    type: VerifyEmailDto,
    description: 'Request to verify user email',
  })
  @ApiResponse({
    status: 200,
    description: 'Email verified successfully',
    schema: {
      type: 'object',
      properties: {
        message: { type: 'string', example: 'Email verified successfully' },
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid token' })
  @ApiResponse({ status: 404, description: 'User not found' })
  async verifyEmail(@Body() body: VerifyEmailDto): Promise<{ message: string }> {
    const { token, email } = body;
    try {
      this.logger.log(`Email verification request for email: ${JSON.stringify(email)}`);
      this.logger.debug(`Verification token: ${JSON.stringify(token)}`);
      if (!email || !token) {
        throw new HttpException('Email and token are required', HttpStatus.BAD_REQUEST);
      }
      const verifyEmailDto = new VerifyEmailDto();
      verifyEmailDto.email = email;
      verifyEmailDto.token = token;
      await this.authService.verifyEmail(verifyEmailDto);
      return { message: 'Email verified successfully' };
    } catch (error) {
      this.logger.error('Error verifying email:', error);
      if (error instanceof HttpException) throw error;
      throw new InternalServerErrorException('Failed to verify email');
    }
  }

  @Post('send-verification-email')
  @ApiOperation({ summary: 'Send verification email' })
  @ApiBody({
    type: String,
    description: 'Request to send verification email',
  })
  @ApiResponse({
    status: 200,
    description: 'Verification email sent successfully',
    schema: {
      type: 'object',
      properties: {
        message: { type: 'string', example: 'Verification email sent successfully' },
      },
    },
  })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid email' })
  @ApiResponse({ status: 404, description: 'User not found' })
  async sendVerificationEmail(@Body('email') email: string): Promise<{ message: string }> {
    this.logger.log(`Request to send verification email to: ${JSON.stringify(email)}`);
    if (!email) {
      throw new HttpException('Email is required', HttpStatus.BAD_REQUEST);
    }

    await this.authService.sendEmailVerification(email);
    return { message: 'Verification email sent successfully' };
  }
}
