import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  UseGuards,
  NotFoundException,
  ForbiddenException,
} from '@nestjs/common';
import { GpsService } from './gps.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { Role } from '@prisma/client';
import { EnrollDriverDto, ConfirmConsentDto } from './dto/enroll-driver.dto';
import { UpdateGpsSettingsDto } from './dto/update-gps-settings.dto';
import { PrismaService } from '../prisma/prisma.service';

@Controller('gps')
@UseGuards(JwtAuthGuard, RolesGuard)
export class GpsController {
  constructor(
    private readonly gpsService: GpsService,
    private readonly prisma: PrismaService,
  ) {}

  // ============================================
  // Admin-only endpoints
  // ============================================

  /**
   * Get GPS system status
   */
  @Get('status')
  @Roles(Role.ADMINISTRATOR)
  async getStatus() {
    return this.gpsService.getStatus();
  }

  /**
   * Get GPS settings
   */
  @Get('settings')
  @Roles(Role.ADMINISTRATOR)
  async getSettings() {
    const settings = await this.gpsService.getSettings();
    // Don't return the password
    return {
      ...settings,
      traccarAdminPassword: settings.traccarAdminPassword ? '********' : null,
    };
  }

  /**
   * Update GPS settings
   */
  @Patch('settings')
  @Roles(Role.ADMINISTRATOR)
  async updateSettings(@Body() dto: UpdateGpsSettingsDto) {
    const settings = await this.gpsService.updateSettings(dto);
    return {
      ...settings,
      traccarAdminPassword: settings.traccarAdminPassword ? '********' : null,
    };
  }

  /**
   * Get all enrolled devices
   */
  @Get('devices')
  @Roles(Role.ADMINISTRATOR)
  async getEnrolledDevices() {
    return this.gpsService.getEnrolledDevices();
  }

  /**
   * Enroll a driver for GPS tracking
   */
  @Post('enroll/:driverId')
  @Roles(Role.ADMINISTRATOR)
  async enrollDriver(
    @Param('driverId') driverId: string,
    @Body() dto: EnrollDriverDto,
  ) {
    return this.gpsService.enrollDriver(driverId, dto.sendSignalMessage ?? true);
  }

  /**
   * Unenroll a driver from GPS tracking
   */
  @Delete('devices/:driverId')
  @Roles(Role.ADMINISTRATOR)
  async unenrollDriver(@Param('driverId') driverId: string) {
    return this.gpsService.unenrollDriver(driverId);
  }

  /**
   * Get all active driver locations (Admin map view)
   */
  @Get('locations')
  @Roles(Role.ADMINISTRATOR)
  async getActiveDriverLocations() {
    return this.gpsService.getActiveDriverLocations();
  }

  /**
   * Get a specific driver's location
   */
  @Get('locations/:driverId')
  @Roles(Role.ADMINISTRATOR)
  async getDriverLocation(@Param('driverId') driverId: string) {
    const location = await this.gpsService.getDriverLocation(driverId);
    if (!location) {
      throw new NotFoundException('Driver not found or not enrolled for GPS tracking');
    }
    return location;
  }

  /**
   * Get a driver's stats (Admin viewing any driver)
   */
  @Get('stats/:driverId')
  @Roles(Role.ADMINISTRATOR)
  async getDriverStats(
    @Param('driverId') driverId: string,
    @Query('from') fromStr?: string,
    @Query('to') toStr?: string,
  ) {
    const from = fromStr ? new Date(fromStr) : undefined;
    const to = toStr ? new Date(toStr) : undefined;
    return this.gpsService.getDriverStats(driverId, from, to);
  }

  // ============================================
  // Traccar Admin Access
  // ============================================

  /**
   * Check Traccar setup status
   */
  @Get('traccar/status')
  @Roles(Role.ADMINISTRATOR)
  async getTraccarSetupStatus() {
    return this.gpsService.checkTraccarSetup();
  }

  /**
   * Perform initial Traccar setup
   */
  @Post('traccar/setup')
  @Roles(Role.ADMINISTRATOR)
  async performTraccarSetup(@CurrentUser() user: any) {
    const success = await this.gpsService.performTraccarSetup(user.email);
    if (!success) {
      throw new NotFoundException('Failed to setup Traccar. It may already be configured.');
    }
    return { success: true, message: 'Traccar setup complete' };
  }

  /**
   * Sync all VIP admins to Traccar
   */
  @Post('traccar/sync-admins')
  @Roles(Role.ADMINISTRATOR)
  async syncAdminsToTraccar() {
    return this.gpsService.syncAllAdminsToTraccar();
  }

  /**
   * Get Traccar admin URL (auto-login for current user)
   */
  @Get('traccar/admin-url')
  @Roles(Role.ADMINISTRATOR)
  async getTraccarAdminUrl(@CurrentUser() user: any) {
    // Get full user from database
    const fullUser = await this.prisma.user.findUnique({
      where: { id: user.id },
    });

    if (!fullUser) {
      throw new NotFoundException('User not found');
    }

    return this.gpsService.getTraccarAutoLoginUrl(fullUser);
  }

  /**
   * Get Traccar session for iframe/proxy access
   */
  @Get('traccar/session')
  @Roles(Role.ADMINISTRATOR)
  async getTraccarSession(@CurrentUser() user: any) {
    const fullUser = await this.prisma.user.findUnique({
      where: { id: user.id },
    });

    if (!fullUser) {
      throw new NotFoundException('User not found');
    }

    const session = await this.gpsService.getTraccarSessionForUser(fullUser);
    if (!session) {
      throw new NotFoundException('Could not create Traccar session');
    }

    return { session };
  }

  // ============================================
  // Driver self-service endpoints
  // ============================================

  /**
   * Get my GPS enrollment status
   */
  @Get('me')
  @Roles(Role.DRIVER, Role.ADMINISTRATOR, Role.COORDINATOR)
  async getMyGpsStatus(@CurrentUser() user: any) {
    // Find driver linked to this user
    const driver = await this.prisma.driver.findFirst({
      where: {
        userId: user.id,
        deletedAt: null,
      },
      include: {
        gpsDevice: true,
      },
    });

    if (!driver) {
      return { enrolled: false, message: 'No driver profile linked to your account' };
    }

    if (!driver.gpsDevice) {
      return { enrolled: false, driverId: driver.id };
    }

    return {
      enrolled: true,
      driverId: driver.id,
      deviceIdentifier: driver.gpsDevice.deviceIdentifier,
      consentGiven: driver.gpsDevice.consentGiven,
      consentGivenAt: driver.gpsDevice.consentGivenAt,
      isActive: driver.gpsDevice.isActive,
      lastActive: driver.gpsDevice.lastActive,
    };
  }

  /**
   * Confirm GPS tracking consent (Driver accepting tracking)
   */
  @Post('me/consent')
  @Roles(Role.DRIVER)
  async confirmMyConsent(
    @CurrentUser() user: any,
    @Body() dto: ConfirmConsentDto,
  ) {
    const driver = await this.prisma.driver.findFirst({
      where: {
        userId: user.id,
        deletedAt: null,
      },
    });

    if (!driver) {
      throw new NotFoundException('No driver profile linked to your account');
    }

    await this.gpsService.confirmConsent(driver.id, dto.consentGiven);

    return {
      success: true,
      message: dto.consentGiven
        ? 'GPS tracking consent confirmed. Your location will be tracked during shift hours.'
        : 'GPS tracking consent revoked. Your location will not be tracked.',
    };
  }

  /**
   * Get my GPS stats (Driver viewing own stats)
   */
  @Get('me/stats')
  @Roles(Role.DRIVER, Role.ADMINISTRATOR, Role.COORDINATOR)
  async getMyStats(
    @CurrentUser() user: any,
    @Query('from') fromStr?: string,
    @Query('to') toStr?: string,
  ) {
    const driver = await this.prisma.driver.findFirst({
      where: {
        userId: user.id,
        deletedAt: null,
      },
    });

    if (!driver) {
      throw new NotFoundException('No driver profile linked to your account');
    }

    const from = fromStr ? new Date(fromStr) : undefined;
    const to = toStr ? new Date(toStr) : undefined;

    return this.gpsService.getDriverStats(driver.id, from, to);
  }

  /**
   * Get my current location
   */
  @Get('me/location')
  @Roles(Role.DRIVER, Role.ADMINISTRATOR, Role.COORDINATOR)
  async getMyLocation(@CurrentUser() user: any) {
    const driver = await this.prisma.driver.findFirst({
      where: {
        userId: user.id,
        deletedAt: null,
      },
    });

    if (!driver) {
      throw new NotFoundException('No driver profile linked to your account');
    }

    const location = await this.gpsService.getDriverLocation(driver.id);
    if (!location) {
      throw new NotFoundException('You are not enrolled for GPS tracking');
    }

    return location;
  }
}
