// backend/src/modules/auth/auth.controller.ts

import {
  Controller,
  Post,
  Body,
  UseGuards,
  Req,
  Res,
  HttpCode,
  HttpStatus,
  Get,
  Patch,
  Put,
} from '@nestjs/common';
import type { Response } from 'express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiBody,
  ApiUnauthorizedResponse,
  ApiBadRequestResponse
} from '@nestjs/swagger';

import { AuthService } from './auth.service';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { AppException, ErrorCode } from '../../common/errors';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { CreateOrganizationDto } from './dto/create-organization.dto';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { LocalAuthGuard } from './guards/local-auth.guard';
import { UpdateLanguageDto } from './dto/update-language.dto';
import {
  ForgotPasswordSendCodeDto,
  ForgotPasswordVerifyDto,
} from './dto/forgot-password.dto';
import { ChangePasswordDto } from './dto/change-password.dto';
import { UpdateParticipationDto } from './dto/update-participation.dto';
import { UpsertWorkingHoursDto } from '../staff/dto/upsert-working-hours.dto';

@ApiTags('auth')
@Controller('auth')
export class AuthController {
  private static readonly REMEMBER_ME_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
  private static readonly PASSWORD_RESET_MAX_AGE_MS = 15 * 60 * 1000;

  constructor(
    private readonly authService: AuthService,
    private readonly configService: ConfigService,
    private readonly jwtService: JwtService,
  ) {}

  // =========================
  // LOGIN
  // =========================
  @Post('login')
  @HttpCode(HttpStatus.OK)
  @UseGuards(LocalAuthGuard)
  @ApiOperation({ summary: 'Login with email and password' })
  @ApiBody({ type: LoginDto })
  @ApiResponse({ status: 200, description: 'Login successful' })
  @ApiUnauthorizedResponse({ description: 'Invalid credentials' })
  @ApiBadRequestResponse({ description: 'Invalid input data' })
  async login(
    @Body() loginDto: LoginDto,
    @Req() req,
    @Res({ passthrough: true }) res: Response
  ) {
    console.log('Login endpoint hit');

    const result = await this.authService.login(loginDto, req.user);
    const rememberMe = Boolean(loginDto.rememberMe);

    this.setAuthCookies(
      res,
      result.data.accessToken,
      result.data.refreshToken,
      rememberMe,
    );

    return {
      success: true,
      data: {
        user: result.data.user,
        organizations: result.data.organizations,
      },
    };
  }

  // =========================
  // REGISTER
  // =========================
  @Post('register')
  @ApiOperation({ summary: 'Register a new user' })
  @ApiBody({ type: RegisterDto })
  @ApiResponse({ status: 201, description: 'User registered successfully' })
  @ApiBadRequestResponse({ description: 'Invalid input data' })
  async register(
    @Body() registerDto: RegisterDto,
    @Res({ passthrough: true }) res: Response
  ) {
    console.log('Register endpoint hit');

    const result = await this.authService.register(registerDto);

    // ✅ SET COOKIES HERE
    this.setAuthCookies(res, result.data.accessToken, result.data.refreshToken);

    return {
      success: true,
      data: {
        user: result.data.user,
        organizations: result.data.organizations,
      },
    };
  }

  // =========================
  // SELECT ORGANIZATION
  // =========================
  @Post('select-organization')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  async selectOrganization(
    @Req() req,
    @Body('organizationId') organizationId: string,
    @Res({ passthrough: true }) res: Response
  ) {
    const result = await this.authService.selectOrganization(
      req.user.id,
      organizationId
    );

    this.setAccessToken(
      res,
      result.data.accessToken,
      this.isPersistentSession(req),
    );

    return {
      success: true,
      data: {
        organization: result.data.organization,
      },
    };
  }

  @Post('organizations')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Create organization for current user' })
  async createOrganization(@Req() req, @Body() dto: CreateOrganizationDto) {
    return this.authService.createOrganization(
      req.user.id,
      req.user.organizationId,
      dto,
    );
  }

  // =========================
  // PROFILE
  // =========================
  @Get('profile')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Get user profile' })
  @ApiResponse({ status: 200, description: 'Profile retrieved successfully' })
  @ApiUnauthorizedResponse({ description: 'Invalid or missing JWT token' })
  async getProfile(@Req() req) {
    const result = await this.authService.getProfile(req.user.id);
    const accessTokenExpiresAt = this.readAccessTokenExpiresAt(req);

    if (!accessTokenExpiresAt || !result.data) {
      return result;
    }

    return {
      ...result,
      data: {
        ...result.data,
        accessTokenExpiresAt,
      },
    };
  }

  @Patch('profile/language')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Update user language preference' })
  async updateLanguage(@Req() req, @Body() dto: UpdateLanguageDto) {
    return this.authService.updateLanguage(req.user.id, dto);
  }

  @Patch('profile/password')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Change account password' })
  async changePassword(
    @Req() req,
    @Body() dto: ChangePasswordDto,
    @Res({ passthrough: true }) res: Response,
  ) {
    const skipCurrentPassword = req?.cookies?.passwordResetVerified === '1';

    const result = await this.authService.changePassword(
      req.user.id,
      dto.currentPassword,
      dto.newPassword,
      skipCurrentPassword,
    );

    this.clearAuthCookies(res);
    res.clearCookie('passwordResetVerified', this.baseCookieOptions());

    return result;
  }

  @Get('profile/participation')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Get owner participation settings for current organization' })
  async getParticipation(@Req() req) {
    return this.authService.getParticipation(req.user.id, req.user.organizationId);
  }

  @Patch('profile/participation')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Enable or disable owner participation in treatments/tasks' })
  async updateParticipation(@Req() req, @Body() dto: UpdateParticipationDto) {
    return this.authService.updateParticipation(
      req.user.id,
      req.user.organizationId,
      dto,
    );
  }

  @Get('profile/working-hours')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Get working hours for participating clinic owner' })
  async getMyWorkingHours(@Req() req) {
    return this.authService.getMyWorkingHours(req.user.id, req.user.organizationId);
  }

  @Put('profile/working-hours')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({ summary: 'Save working hours for participating clinic owner' })
  async upsertMyWorkingHours(@Req() req, @Body() dto: UpsertWorkingHoursDto) {
    return this.authService.upsertMyWorkingHours(
      req.user.id,
      req.user.organizationId,
      dto,
    );
  }

  @Post('forgot-password/send-code')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Send forgot-password SMS verification code' })
  async sendForgotPasswordCode(@Body() dto: ForgotPasswordSendCodeDto) {
    return this.authService.sendForgotPasswordCode(dto);
  }

  @Post('forgot-password/verify')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Verify SMS code and sign in for password reset' })
  async verifyForgotPasswordCode(
    @Body() dto: ForgotPasswordVerifyDto,
    @Res({ passthrough: true }) res: Response,
  ) {
    const result = await this.authService.verifyForgotPasswordCode(dto);

    this.setAuthCookies(
      res,
      result.data.accessToken,
      result.data.refreshToken,
    );

    res.cookie('passwordResetVerified', '1', {
      ...this.baseCookieOptions(),
      maxAge: AuthController.PASSWORD_RESET_MAX_AGE_MS,
    });

    return {
      success: true,
      data: {
        user: result.data.user,
        organizations: result.data.organizations,
        redirectTo: '/settings/account',
      },
    };
  }

  @Get('subscription-alert')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('JWT-auth')
  @ApiOperation({
    summary:
      'Owner-only: seat / trial status for warning indicator (current org from JWT)',
  })
  async getSubscriptionAlert(@Req() req) {
    return this.authService.getOwnerSubscriptionAlert(
      req.user.id,
      req.user.organizationId,
    );
  }

  // =========================
  // REFRESH
  // =========================
  @Post('refresh')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Refresh access token using refresh cookie' })
  @ApiResponse({ status: 200, description: 'Access token refreshed' })
  @ApiUnauthorizedResponse({ description: 'Invalid or missing refresh token' })
  async refresh(@Req() req, @Res({ passthrough: true }) res: Response) {
    const refreshToken = req?.cookies?.refreshToken;

    if (!refreshToken) {
      throw new AppException(ErrorCode.AUTH_REFRESH_TOKEN_NOT_FOUND, HttpStatus.UNAUTHORIZED);
    }

    const result = await this.authService.refreshToken(
      refreshToken,
      req?.cookies?.accessToken,
    );

    this.setAccessToken(
      res,
      result.data.accessToken,
      this.isPersistentSession(req),
    );

    return {
      success: true,
      data: {
        accessToken: result.data.accessToken,
        accessTokenExpiresAt: result.data.accessTokenExpiresAt,
      },
    };
  }

  private readAccessTokenExpiresAt(req: { cookies?: { accessToken?: string } }): string | undefined {
    const token = req?.cookies?.accessToken;
    if (!token) {
      return undefined;
    }

    const payload = this.jwtService.decode(token) as { exp?: number } | null;
    if (!payload?.exp) {
      return undefined;
    }

    return new Date(payload.exp * 1000).toISOString();
  }

  // =========================
  // LOGOUT
  // =========================
  @Post('logout')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Logout current user' })
  @ApiResponse({ status: 200, description: 'Logout successful' })
  async logout(@Req() req, @Res({ passthrough: true }) res: Response) {
    const accessToken = req?.cookies?.accessToken;

    if (accessToken) {
      await this.authService.logout(accessToken);
    }

    this.clearAuthCookies(res);

    return {
      success: true,
      message: 'Logged out successfully',
    };
  }

  // =========================
  // TEST
  // =========================
  @Get('test')
  @ApiOperation({ summary: 'Test endpoint' })
  test() {
    return { message: 'Auth controller is working!' };
  }

  // =========================
  // 🔥 COOKIE HELPERS
  // =========================
  private isPersistentSession(req: { cookies?: Record<string, string> }): boolean {
    return req?.cookies?.authRemember === '1';
  }

  private baseCookieOptions() {
    return {
      httpOnly: true,
      secure: this.configService.get<boolean>('cookie.secure') ?? false,
      sameSite: 'lax' as const,
      path: '/',
    };
  }

  private setAuthCookies(
    res: Response,
    accessToken: string,
    refreshToken: string,
    rememberMe = false,
  ) {
    this.setAccessToken(res, accessToken, rememberMe);
    this.setRefreshToken(res, refreshToken, rememberMe);
    this.setRememberMeFlag(res, rememberMe);
  }

  private setAccessToken(res: Response, token: string, rememberMe = false) {
    res.cookie('accessToken', token, {
      ...this.baseCookieOptions(),
      ...(rememberMe
        ? { maxAge: AuthController.REMEMBER_ME_MAX_AGE_MS }
        : {}),
    });
  }

  private setRefreshToken(res: Response, token: string, rememberMe = false) {
    res.cookie('refreshToken', token, {
      ...this.baseCookieOptions(),
      ...(rememberMe
        ? { maxAge: AuthController.REMEMBER_ME_MAX_AGE_MS }
        : {}),
    });
  }

  private setRememberMeFlag(res: Response, rememberMe: boolean) {
    if (rememberMe) {
      res.cookie('authRemember', '1', {
        ...this.baseCookieOptions(),
        maxAge: AuthController.REMEMBER_ME_MAX_AGE_MS,
      });
      return;
    }

    res.clearCookie('authRemember', this.baseCookieOptions());
  }

  private clearAuthCookies(res: Response) {
    const options = this.baseCookieOptions();
    res.clearCookie('accessToken', options);
    res.clearCookie('refreshToken', options);
    res.clearCookie('authRemember', options);
    res.clearCookie('passwordResetVerified', options);
  }
}